При использовании проверки или [ для оценки условных выражений с логическим НЕ! Должен ли восклицательный знак помещаться внутри или снаружи квадратных скобок?

Как вы определяете «полностью» 64 -бит?

% file /usr/bin/perl
/usr/bin/perl: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [i386:Mach-O executable i386]
/usr/bin/perl (for architecture x86_64):        Mach-O 64-bit executable x86_64
/usr/bin/perl (for architecture i386):  Mach-O executable i386
% sw_vers
ProductName:    Mac OS X
ProductVersion: 10.13.5
BuildVersion:   17F77
3
18.05.2020, 19:47
3 ответа

! [ "$var" -gt 0 ]и [ ! "$var" -gt 0 ]не должны иметь значения для текущих оболочек и утилит, если нет ошибок.


Но обратите внимание, что вы написали if ! [ $var -gt 0 ]и if [ ! $var -gt 0 ]без кавычек вокруг $varи что имеет значение !

Это могло бы сломаться, если бы $varбыло пустым или содержало пробелы. Вы получите, например. if ! [ -gt 0 ], где тест [напечатает сообщение об ошибке и вернет ложный код выхода 2. Затем он будет инвертирован !, и условие в целом будет истинным!

$ var=
$ if ! [ $var -gt 0 ]; then echo yes; fi
bash: [: -gt: unary operator expected
yes

В этом конкретном контексте [ ! $var -gt 0 ]можно рассматривать как более безопасный, поскольку возврат ложной ошибки останется ложным, а ветвь не будет выполнена. Здесь непосредственная проблема заключается в отсутствии кавычек, и это легко исправить.

О кавычках см.:Когда необходимо двойное -цитирование?


Однако, даже помимо проблемы цитирования, не-числовые значения в $varтакже давали бы ошибку из [и делали бы условие ложным. Из-за этого вы должны заранее проверить правильность входных данных или иным образом обработать эту возможность. В некоторых случаях правильное действие в случае ошибки может состоять в том, чтобы выбрать ветвь, например. если он содержал ошибку выхода. К сожалению, у оболочки нет хорошего способа обнаружения ошибок, так как нет никаких исключений или чего-то подобного. Чтобы отличить ошибку от ложного результата, необработанное значение в $?необходимо сохранить и просмотреть.

Или тест можно написать так, чтобы случай ошибки совпадал с соответствующим результатом. Например, здесь тест написан с дополнительным отрицанием вместо [ "$1" -le 0 ], но это имеет то преимущество, что недопустимый ввод вызовет выход по ошибке. Это работает только с !снаружи [.

$ cat foo.sh
#!/bin/bash

if ! [ "$1" -gt 0 ]; then
    echo "invalid input '$1'"
    exit 1
fi
echo "doing work on '$1'"

$ bash foo.sh
foo.sh: line 3: [: : integer expression expected
invalid input ''

Вариации значений переменной и различные тесты в матрице:

   test   \   $n    |   '0'       '1'      'x'     
 -------------------+------------------------------------            
  [   "$n" -le 0 ]  |  truthy    falsy    falsy  (w/error)
  [ ! "$n" -gt 0 ]  |  truthy    falsy    falsy  (w/error)
! [   "$n" -gt 0 ]  |  truthy    falsy    truthy (w/error)

Возможно, в какой-то мере это связано с тем, что в былые времена версии [, как известно, путались, когда операнды в тесте выглядели как операторы. Например. с чем-то вроде [ ! = x ]!может быть воспринято как отрицание, и выражение в целом не может быть проанализировано. Можно опасаться, что размещение !внутри [может усугубить эту проблему, но я не знаю, произошло бы это на практике. В любом случае версии [, соответствующие POSIX -, должны быть защищены от этого, по крайней мере, до тех пор, пока вы не используете -aи -o.

См.:Какова цель добавления префикса с обеих сторон сравнения переменных оболочки к строковому литералу? , особенно отличный ответ Стефана (, из которого я взял [ ! = x ]пример ).

1
28.04.2021, 23:13

Это имеет значение, только если вы заботитесь о том, что оценивает !, что вы можете сделать, если вы принимаете во внимание сбои в утилите [, (, например. $varна самом деле не является целым числом, и в этом случае утилита [потерпит неудачу, и ее статус выхода будет не -нулем, то есть не потому, что тест оценивается как false , а потому, что утилита не удалась выполнить тест ).

В if [ ! "$var" -gt 0 ]; then...; fi!является аргументом для [и, следовательно, будет оцениваться утилитой [, заставляя ее внутренне отрицать смысл теста перед завершением с соответствующим статусом выхода. (Если $varне является целым числом, тест завершится неудачно, и переход не будет выполнен.)

В if ! [ "$var" -gt 0 ]; then...; fiоболочка будет отрицать (хорошо, переключать между 0 и не -нулем )статус выхода утилиты [перед использованием его для оператора if. (Если $varне является целым числом, тест завершится ошибкой, оболочка отменит не-нулевой статус выхода и интерпретирует его как true , и будет выполнено ветвление.)

Для этого простого теста я, вероятно, переписал бы тест как [ "$var" -le 0 ]для простоты чтения и понимания кода и, если необходимо, использовал проверку $varкак целое число.

0
28.04.2021, 23:13

Да, это имеет значение.

Если вы поместите !вне [... ], то любая ошибка будет проигнорирована:

var=nono

if ! [ "$var" -gt 0 ]; then echo YES; fi
bash: [: 0.1: integer expression expected
YES

if [ ! "$var" -gt 0 ]; then echo YES; fi
bash: [: 0.1: integer expression expected

Это потому, что if ! command; then...будет соответствовать, когда статус выхода commandне равен -нулю,а команда [ expr ]будет иметь ненулевой статус выхода, отличный от -, как в случае ошибки, так и в случае, когда exprоценивается как ложное.

Только в ksh (НЕ в bash )два вида похожи, потому что в ksh числовая переменная, не являющаяся -, будет рассматриваться как 0в этом контексте, а не как ошибка.

3
28.04.2021, 23:13

Теги

Похожие вопросы