Неужели синтаксис не равен материи?

22

При написании сценариев я обычно пишу свои if со следующим синтаксисом, так как мне легче понять, что то, что будет дальше, не соответствует действительности.

if [ ! "$1" = "$2" ]; then

Другие говорят, что путь ниже лучше

if [ "$1" != "$2" ]; then

Дело в том, что когда я спрашиваю, почему и есть ли различия, никто, похоже, не может ответить.

Итак, есть ли различия между двумя синтаксисами? Один из них безопаснее другого? Или это просто вопрос предпочтений / привычки?

Jimmy_A
источник
9
В первом случае вы должны знать , операторы приоритет отличать !(x==y)от (!x)==y.
Джимми
@jimmij Вы имеете в виду, что при сравнении строки это if [ ! "$string" = "one" ]переводит, если не значение $stringравно one. И это if [ "$string" != "one"]означает, что значение $stringне равно one?
Jimmy_A
5
@Jimmy_A Я имею в виду, что вам нужно знать, как это «переводит». Сбор операторов вместе ( !=синтаксис) просто более очевиден.
Джимми
Уважаемые близкие избиратели, ответ Стефана показывает, что в поведении есть существенная разница, правильный ответ никоим образом не основан на мнении.
Кевин

Ответы:

35

Помимо косметических аргументов / аргументов предпочтения, одной из причин может быть то, что существует больше реализаций, где [ ! "$a" = "$b" ]в угловых случаях происходит сбой, чем в [ "$a" != "$b" ].

Оба случая должны быть безопасными, если реализации следуют алгоритму POSIX , но даже сегодня (в начале 2018 года на момент написания) все еще существуют реализации, которые терпят неудачу. Например, с a='(' b=')':

$ (a='(' b=')'; busybox test "$a" != "$b"; echo "$?")
0
$ (a='(' b=')'; busybox test ! "$a" = "$b"; echo "$?")
1

В dashверсиях до 0.5.9, таких как 0.5.8, как shв Ubuntu 16.04, например:

$ a='(' b=')' dash -c '[ "$a" != "$b" ]; echo "$?"'
0
$ a='(' b=')' dash -c '[ ! "$a" = "$b" ]; echo "$?"'
1

(исправлено в 0.5.9, см. https://www.mail-archive.com/dash@vger.kernel.org/msg00911.html )

Эти реализации обрабатываются [ ! "(" = ")" ]следующим образом (проверяют [ ! "(" "text" ")" ], является [ ! "text" ]ли «текст» пустой строкой), в то время как POSIX предписывает это [ ! "x" = "y" ](проверяет «x» и «y» на равенство). Эти реализации терпят неудачу, потому что они выполняют неправильный тест в этом случае.

Обратите внимание, что есть еще одна форма:

! [ "$a" = "$b" ]

Для этого требуется оболочка POSIX (не будет работать со старой оболочкой Bourne).

Обратите внимание, что в нескольких реализациях также были проблемы с [ "$a" = "$b" ][ "$a" != "$b" ]), и они по-прежнему похожи на [встроенные в /bin/shSolaris 10 (оболочка Bourne, в которой находится оболочка POSIX /usr/xpg4/bin/sh). Вот почему вы видите такие вещи, как:

[ "x$a" != "x$b" ]

В скриптах стараюсь быть переносимым на старые системы.

Стефан Шазелас
источник
Другими словами, оба синтаксиса делают одно и то же, без различий, но с ! "$a" = "b"вами нужно быть более внимательным к тому, как вы пишете его, чтобы определить сравнение. Из вашего примера я понимаю, что возвращается вторая команда, not zeroкоторая может быть как полезной, так и тревожной. Это может быть полезно, если вы хотите выйти, если они не совпадают, или беспокоиться, если вы хотите увидеть, не сработало ли сравнение
сработало Jimmy_A
2
@Jimmy_A, нет, в этих реализациях [ ! "$a" = "$b" ]просто происходит сбой, когда $aесть (и $bесть ), он утверждает, что они идентичны, когда они не совпадают, не (является той же строкой, что и ), но [в этом случае выполняет неправильный тест.
Стефан Шазелас
Аааа, я думаю, что понял. Первое и третье означают проверку того, является ли условие истинным, а второе и четвертое - ложным. Таким образом, коды состояния выхода отличаются. По сути, переменные 1 и 4 различны, а 2 и 3 не равны.
Jimmy_A
3
@ Джимми_А нет. Вы полностью упустили суть. Если бы эти реализации следовали POSIX, тогда все коды состояния были бы 0.
Подстановочный
Чтобы убедиться, что я понимаю, это сводится к следующему: [ ! "$a" = "$b" ]иногда неправильно обрабатывается в ошибочных реализациях оболочки (которые, я думаю, более или менее повторяют первое предложение ответа).
Майкл Берр
8

x != yСинтаксис лучше , потому что ! x == yэто чревата ошибки - требует знаний операторов старшинства , которые отличаются от языка к языку. Синтаксис ! x == yможет быть истолковано как !(x == y)или (!x) == y, в зависимости от приоритета !против =.


Например, в c++ отрицании ! предшествует оператор сравнения / реляции == , отсюда следующий код:

#include<iostream>

using namespace std;

int main()
{
  int x=1, y=2;
  if(     x  != y   ) cout<<"true"<<endl; else cout<<"false"<<endl;
  if(  !  x  == y   ) cout<<"true"<<endl; else cout<<"false"<<endl;
  if(  !( x  == y ) ) cout<<"true"<<endl; else cout<<"false"<<endl;
  if(   (!x) == y   ) cout<<"true"<<endl; else cout<<"false"<<endl;
}

возвращается

true
false
true
false

Подобное поведение наблюдается во многих других языках, включая, например, awkчасто используемый инструмент в мире Unix.


С другой стороны, сбор операторов вместе с помощью x != yне приводит к путанице в качестве устоявшейся модели. Более того, технически говоря, !=это очень часто не два, а один оператор, поэтому оценка должна быть даже немного быстрее, чем отдельное сравнение, а затем отрицание. Следовательно, хотя оба синтаксиса работают в bash, я бы порекомендовал следовать, так x != yкак это намного легче читать и поддерживать код, который следует некоторой стандартной логике.

jimmij
источник
4

Такого рода вещи очень на основе мнений, как «ответ» очень сильно зависит от пути мозг индивида , случается быть подключен. Хотя это верно, что семантически, NOT ( A == B )идентично(A != B ) , один может быть яснее одному человеку , а другой к другому. Это также зависит от контекста. Например, если у меня установлен флаг, значения могут быть более понятными с одним синтаксисом над другим:

if NOT ( fileHandleStatus == FS_OPEN )

в отличие от

if ( fileHandleStatus != FS_OPEN )
DopeGhoti
источник
И даже if ( FS_OPEN != fileHandleStatus )в результате простоты случайного набора текста =вместо того, чтобы ==в языках, где первый - это присвоение, а более поздний тест на равенство (как C) ...
Дероберт