Чи не рівнозначний синтаксис?


22

Під час написання сценарію я зазвичай пишу свої ifs з наступним синтаксисом, оскільки мені простіше зрозуміти, що наступне - це неправда.

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

Інші кажуть, що шлях нижче є кращим

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

Річ у тім, коли я запитую, чому і чи є якісь відмінності, нібито ніхто не має відповіді.

Отже, чи є різниці між двома синтаксисами? Чи один із них безпечніший за інший? Або це просто питання уподобань / звички?


9
У першому випадку ви повинні знати , оператори пріоритет відрізняти !(x==y)від (!x)==y.
jimmij

@jimmij Ви маєте на увазі, що при порівнянні рядка це означає, що if [ ! "$string" = "one" ]не значення $stringрівне one. І це if [ "$string" != "one"]означає, якщо значення $stringне дорівнює one?
Jimmy_A

5
@Jimmy_A Я маю на увазі, що вам потрібно знати, як це "перекладається". Збір операторів разом ( !=синтаксис) просто очевидніший.
jimmij

Шановні близькі виборці, відповідь Стефана показує, що істотна різниця у поведінці, правильна відповідь жодним чином не ґрунтується на думці.
Кевін

Відповіді:


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" ](тест «х» і «у» рівності). Ці реалізації не вдається, оскільки вони виконують неправильний тест у такому випадку.

Зауважте, що існує ще одна форма:

! [ "$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що може бути як корисним, так і тривожним. Це може бути корисним, якщо ви хочете вийти, якщо вони не збігаються, або тривожно, якщо ви хочете дізнатися, чи
збігшись

2
@Jimmy_A, ні, в цих реалізаціях [ ! "$a" = "$b" ]просто не вдається, коли $aє (і $bє ), він стверджує, що вони ідентичні, коли їх немає, (не є тим самим рядком ), але [в цьому випадку виконується неправильний тест.
Стефан Шазелас

А-а-а, я думаю, я це зрозумів. Перший і третій означають перевірити, чи умова істинна, тоді як другий і четвертий, якщо умова помилкові. Таким чином, коди статусу виходу відрізняються. В основному кажучи, змінні 1 і 4 різні, а 2 і 3 не рівні.
Jimmy_A

3
@Jimmy_A, ні. Ви повністю пропустили справу. Якби ці реалізації виконували POSIX, то всі коди статусу становитимуть 0.
Wildcard

Щоб переконатися, що я розумію, це зводиться до: [ ! "$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оскільки це простіше читати та підтримувати код, що відповідає деякій стандартній логіці.


4

Така річ дуже ґрунтується на думках, оскільки "відповідь" дуже сильно залежить від способу провідного мозку людини. Хоча це правда, що семантично NOT ( A == B )ідентично (A != B ), одна людина може бути зрозумілішою, а інша - іншій. Це також залежить від контексту. Наприклад, якщо у мене встановлено прапор, значення можуть бути більш чіткими з одного синтаксису над іншим:

if NOT ( fileHandleStatus == FS_OPEN )

на відміну від

if ( fileHandleStatus != FS_OPEN )

І навіть if ( FS_OPEN != fileHandleStatus )у результаті простоти випадкового введення тексту =замість ==мов, де перше - це присвоєння, а пізніше тест на рівність (як C) ...
derobert
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.