Почему не заданные и пустые переменные численно равны нулю с двойными скобками - PullRequest
2 голосов
/ 28 января 2020

Следующий код (проверено с bash, zsh и k sh, результаты могут отличаться для других оболочек) возвращает a is 0 (num). То же самое происходит для a=''. Результаты назначения a=0 или a=1 предсказуемы. Цитирование выражений не меняет результат. Итак, почему двойные скобки рассматривают нулевые и пустые переменные как численно равные нулю?

unset a
#a=''
#a=0
#a=1
if [[ $a == 1 ]] ; then 
   echo 'a is 1 (string)' 
fi
if [[ "$a" == 0 ]] ; then 
   echo 'a is 0 (string)' 
fi
if [[ $a -eq 1 ]] ; then 
   echo 'a is 1 (num)' 
fi
if [[ "$a" -eq 0 ]] ; then 
   echo 'a is 0 (num)' 
fi

Я сознательно избегаю более широких вопросов, связанных с одинарными и двойными скобками, поскольку это очень хорошо освещено в других местах на этом сайте и в других местах. Удивительно, но я не смог найти ничего, что документирует это конкретное поведение.

Дополнительные доказательства:

unset a ; if [[ $a -gt -1 ]] ; then echo 'a > -1' ; fi
unset a ; if [[ $a -lt  1 ]] ; then echo 'a <  1' ; fi

Ответы [ 3 ]

3 голосов
/ 28 января 2020

Я не верю, что это явно задокументировано, но оператор -eq вызывает квазиарифметический контекст c для своих операндов. Обратите внимание:

$ [[ "(3 + 5)" -eq 8 ]] && echo qed
qed

Поведение переменных задокументировано в ARITHMETI C ОЦЕНКА:

Переменная оболочки, которая является нулевой или неустановленной, имеет значение 0 при обращении по имени без использования синтаксис расширения параметра.

, хотя не очевидно, что это также должно применяться к строке, являющейся результатом расширения параметра, и действительно поведение отличается в выражении арифметического c или арифметическом c сама команда:

$ unset a
$ (( a == 0 )) && echo zero
zero
$ (( $a == 0 )) && echo zero
bash: ((: == 0 : syntax error: operand expected (error token is "== 0 ")

Учитывая, что у вас уже есть $((...)) и ((...)) для арифметики c, лучше избегать -eq и других арифметических c операторов сравнения внутри [[; либо используйте [ "$a" -eq 0 ] (что вызовет ошибку, если a равно нулю или не установлено), либо используйте (( a == 0 ).

2 голосов
/ 28 января 2020

Это хорошо документировано в руководстве: 6,5 Арифметика оболочки c

Внутри выражения на переменные оболочки также можно ссылаться по имени без использования синтаксиса расширения параметра. Переменная оболочки, которая является нулевой или неустановленной, имеет значение 0 при обращении по имени без использования синтаксиса расширения параметра.

и

Нулевое значение равно 0.

Ваши тесты выполняются во втором случае.

Я не могу сказать вам обоснование дизайна этого. На практике это удобно:

unset a
(( a++ ))
echo $a    # => 1

Кроме того, другие языки делают то же самое:

$ awk 'BEGIN {if (unset_variable == 0) print "zero"}'
zero

$ perl -E 'if ($unset_variable == 0) {say "zero"}'
zero
0 голосов
/ 28 января 2020

Итак, почему двойные скобки рассматривают нулевые и пустые переменные как численно равные нулю?

Из bash руководство по оболочке 6.5 Shell Arithmeti c:

Переменная оболочки, которая является нулевой или неустановленной, имеет значение 0 при обращении к ней по имени без использования синтаксиса раскрытия параметров

Кавычки внутри составной команды bash extension [[ не имеют значения, так как внутри [[.

раскрытие слов не выполняется.
...