Почему [-n] неверно, как [-n “”]?


20

Мой вопрос о возвращаемых значениях, полученных этим кодом:

if [ -n ]; then echo "true"; else echo "false"; fi

Это печатает true.

Его дополнительный тест, использующий [ -z ]также принты true:

if [ -z ]; then echo "true"; else  echo "false"; fi

В приведенном выше коде, почему [ -n ]тест принимает строковое значение, которое вообще не передается, а не ноль?

Код ниже печатает false. Это ожидается, поскольку переданное строковое значение равно нулю и имеет нулевую длину.

if [ -n "" ]; then echo "true"; else  echo "false"; fi

Ответы:


14

[x] эквивалентно [ -nx,] даже если x начинается с того, -что нет операнда.

$ [ -o ] ; echo $?
0
$ [ -eq ] ; echo $?
0
$ [ -n -o ] ; echo $?
0
$ [ -n -eq ] ; echo $?
0

4
Обратите внимание, что исторически [ -t ]проверялось, является ли stdout терминалом (сокращенно [ -t 1 ]), и некоторые оболочки все еще делают это (в случае, ksh93когда это -tбуквально), так что лучше использовать, [ -n "$var" ]чем [ "$var" ]. Хотя это все равно не в некоторых старых testреализациях для значений , $varкак =, в этом случае , [ "" != "$var" ]или [ "x$var" != x ]или case $x in "")...может быть лучше.
Стефан

3
Интересный выбор -oздесь. В некоторых оболочках, например bash, -oесть как унарный (тест, если опция установлена), так и бинарный (или) оператор, поэтому такие вещи [ ! -o monitor ]неоднозначны. Является ли это тестирование , что monitorопция не установлена, или что либо !или monitorне являются пустыми строками? Правила POSIX решают: последнее (это отличается от [[ ! -o monitor ]]).
Стефан

30

[ -n ]не использует -nтест.

-nВ [ -n ]это не тест на всех. Если между [и есть только один аргумент ], этот аргумент является строкой, которая проверяется, чтобы определить, является ли она пустой. Даже если эта строка имеет ведущий -, она все равно интерпретируется как операнд, а не тест. Поскольку строка -nне пуста - она содержит два символа, -и n, не нулевой characters-- имеет [ -n ]значение верно.

Как говорит Игнасио Васкес-Абрамс , где stringодин аргумент, тест, выполняемый в stringin, совпадает с тестом, выполненным для него . Когда это случается , ничего особенного не происходит. В и второй в просто строки, испытываемых пустоты.[ string ][ -n string ]string-n-n[ -n ]-n[ -n -n ]

Когда между [и есть только один аргумент ], этот аргумент всегда является строкой, которая должна быть проверена на непустоту, даже если она названа так же, как и тест. Точно так же, когда между [и ]и есть два аргумента, и первый из них есть -n, второй всегда является строкой, которая должна быть проверена на непустоту, даже если она названа так же, как и тест. Это просто потому, что синтаксис for [настаивает на том, что один аргумент между [и ]после -nявляется строковым операндом.

По той же причине, по [ -n ]которой не используется -nтест, [ -z ]не используется -zтест.


Вы можете узнать больше о [в bash, исследуя помощь для него. Обратите внимание, что это встроенная оболочка :

$ type [
[ is a shell builtin

Таким образом, вы можете запустить, help [чтобы получить помощь по этому вопросу:

$ help [
[: [ arg... ]
    Evaluate conditional expression.

    This is a synonym for the "test" builtin, but the last argument must
    be a literal `]', to match the opening `['.

Для получения дополнительной информации, в том числе о том, какие тесты поддерживаются и как они работают, вам необходимо обратиться к справке test. Когда вы запустите команду help test, вы получите подробный список. Вместо того, чтобы воспроизводить все это, вот часть о строковых операторах:

      -z STRING      True if string is empty.

      -n STRING
         STRING      True if string is not empty.

      STRING1 = STRING2
                     True if the strings are equal.
      STRING1 != STRING2
                     True if the strings are not equal.
      STRING1 < STRING2
                     True if STRING1 sorts before STRING2 lexicographically.
      STRING1 > STRING2
                     True if STRING1 sorts after STRING2 lexicographically.

Обратите внимание на это -n STRINGи просто STRINGсделайте то же самое: они проверяют, не является ли строка STRINGпустой.


2
Хорошее объяснение
Сергей Колодяжный

1
Возможно также упомяните, что это имеет значение для переменных без кавычек,
Сергей Колодяжный

1
@SergiyKolodyazhnyy Я думаю, что может быть лучше в отдельном ответе. Переменные, которые не установлены, или установлены, но пусты, или содержат только IFSпробел, будут делать это; Переменные, которые содержат несколько слов вместо нуля, производят другой эффект и могут привести к запуску совершенно другого теста. Строки, которые появляются буквально с намерением быть операндами, будут работать правильно только в том случае, если они не содержат пробелов или табуляции или если они заключены в кавычки. Ответ стал бы намного длиннее и сложнее, если бы я освещал эту тему доступным способом. Если вы решили написать ответ, не стесняйтесь @ me здесь об этом!
Элия ​​Каган,

@ Элия Каган, ваш ответ является более полным и подробным, хотя ответ Игнасио Васкеса-Абрамса ответил на мой вопрос.
Рави Кумар

1
Я думаю, что вы упускаете причину, по которой это так и должно быть: если нет, [ "$var" ]то никогда не будет использоваться в качестве теста, потому что $varможет расшириться до -n.
R ..

12

[ -n ]истина, потому что [команда (она же testкоманда) действует в зависимости от количества аргументов, которые ей даны. Если ему дается только один аргумент, результатом будет "true", если аргумент является непустой строкой. «-n» - это строка из 2 символов, не пустая, поэтому «true».

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.