Czy składnia różnej materii jest ważna?

22

Podczas pisania skryptów zazwyczaj piszę ifs z następującą składnią, ponieważ łatwiej mi zrozumieć, że to, co nastąpi później, nie jest prawdą.

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

Inni twierdzą, że sposób poniżej jest lepszy

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

Chodzi o to, kiedy pytam, dlaczego i czy są jakieś różnice, nikt nie wydaje się mieć żadnej odpowiedzi.

Czy są jakieś różnice między tymi dwiema składniami? Czy jeden z nich jest bezpieczniejszy od drugiego? Czy to tylko kwestia preferencji / nawyku?

Jimmy_A
źródło
9
W pierwszym przypadku trzeba wiedzieć podmiotom pierwszeństwo odróżnić !(x==y)od (!x)==y.
jimmij
@jimmij Czy masz na myśli, że przy porównywaniu łańcucha if [ ! "$string" = "one" ]przekłada się to na, jeśli nie, wartość $stringrówną one. A to if [ "$string" != "one"]przekłada się na to, czy wartość $stringnie jest równa one?
Jimmy_A
5
@ Jimmy_A Mam na myśli, że musisz wiedzieć, jak to „tłumaczy”. Zbieranie operatorów razem ( !=składnia) jest po prostu bardziej oczywiste.
jimmij
Drodzy bliscy wyborcy, odpowiedź Stéphane'a pokazuje, że istnieje istotna różnica w zachowaniu, prawidłowa odpowiedź nie jest w żaden sposób oparta na opiniach.
Kevin

Odpowiedzi:

35

Oprócz argumentów kosmetycznych / preferencji, jednym z powodów może być to, że jest więcej implementacji, w których [ ! "$a" = "$b" ]zawodzi się w przypadkach narożnych niż z [ "$a" != "$b" ].

Oba przypadki powinny być bezpieczne, jeśli implementacje są zgodne z algorytmem POSIX , ale nawet dzisiaj (na początku 2018 r. W momencie pisania) nadal istnieją implementacje, które zawodzą. Na przykład z a='(' b=')':

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

Na przykład w dashwersjach wcześniejszych niż 0.5.9, takich jak 0.5.8, jak shna przykład Ubuntu 16.04:

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

(naprawiony w wersji 0.5.9, patrz https://www.mail-archive.com/[email protected]/msg00911.html )

Te implementacje traktują [ ! "(" = ")" ]tak [ ! "(" "text" ")" ], jak jest [ ! "text" ](sprawdź, czy „tekst” jest łańcuchem pustym), podczas gdy POSIX tak nakazuje [ ! "x" = "y" ](przetestuj „x” i „y” pod kątem równości). Te implementacje kończą się niepowodzeniem, ponieważ w tym przypadku wykonują zły test.

Pamiętaj, że istnieje jeszcze jedna forma:

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

Ten wymaga powłoki POSIX (nie będzie działać ze starą powłoką Bourne'a).

Zauważ, że kilka implementacji miało również problemy z [ "$a" = "$b" ](i [ "$a" != "$b" ]) i nadal działa podobnie jak [wbudowane /bin/shw Solaris 10 (powłoka Bourne'a, w której znajduje się powłoka POSIX /usr/xpg4/bin/sh). Dlatego widzisz takie rzeczy jak:

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

W skryptach próbuje być przenośny na stare systemy.

Stéphane Chazelas
źródło
Innymi słowy, obie składnie robią to samo, bez żadnych różnic, ale ! "$a" = "b"musisz być bardziej ostrożny przy pisaniu, aby określić porównanie. Z twojego przykładu rozumiem, że drugie polecenie powraca, not zeroco może być zarówno korzystne, jak i kłopotliwe. Może być korzystne, jeśli chcesz wyjść, jeśli się nie zgadzają, lub niepokojące, jeśli chcesz zobaczyć, czy porównanie się
zawiesiło
2
@ Jimmy_A, nie, w tych implementacjach [ ! "$a" = "$b" ]po prostu się nie udaje, kiedy $ajest (i $bjest ), twierdzi, że są identyczne, gdy nie są, nie (jest taki sam jak ), ale [w tym przypadku wykonuje zły test.
Stéphane Chazelas,
Ahhh, chyba to rozumiem. Pierwszy i trzeci oznaczają sprawdzenie, czy warunek jest prawdziwy, a drugi i czwarty, jeśli warunek jest fałszywy. Zatem kody statusu wyjścia są różne. Zasadniczo mówiąc, zmienne 1 i 4 są różne, a 2 i 3 nie są równe.
Jimmy_A
3
@ Jimmy_A, no. Całkowicie przegapiłeś ten punkt. Jeśli te implementacje były zgodne z POSIX, wówczas wszystkie kody stanu miałyby wartość 0.
Wildcard
Aby upewnić się, że rozumiem, sprowadza się to do: [ ! "$a" = "$b" ]czasami jest niepoprawnie traktowane w błędnych implementacjach powłoki (co moim zdaniem jest mniej więcej powtórzeniem pierwszego zdania odpowiedzi).
Michael Burr
8

x != ySkładnia jest lepiej, bo ! x == yjest podatny na błędy - wymaga wiedzy operatorów pierwszeństwa, które różni się od języka na język. Składnia ! x == ymoże być interpretowany jako !(x == y)lub (!x) == y, w zależności od priorytetu !vs =.


Na przykład c++negacja ! występuje przed operatorem porównania / relacji == , stąd następujący kod:

#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;
}

zwraca

true
false
true
false

Podobne zachowanie można zaobserwować w wielu innych językach, w tym np. awk- często używanym narzędziu w świecie uniksowym.


Z drugiej strony, gromadzenie operatorów razem poprzez x != ynie prowadzi do zamieszania jako dobrze ugruntowanego wzorca. Co więcej, technicznie rzecz biorąc !=bardzo często nie jest to dwa, ale tylko jeden operator, więc powinno być nawet marginalnie szybsze do oceny niż oddzielne porównanie, a następnie negacja. Dlatego, mimo że obie składnie działają w trybie bash, zalecam ich przestrzeganie, x != yponieważ łatwiej jest czytać i utrzymywać kod zgodny ze standardową logiką.

jimmij
źródło
4

Tego rodzaju rzeczy są bardzo oparte na opiniach, ponieważ „odpowiedź” zależy bardzo silnie od sposobu, w jaki mózg jednostki jest okablowany. Chociaż prawdą jest, że semantycznie NOT ( A == B )jest identyczny (A != B ), jeden może być bardziej zrozumiały dla jednej osoby, a drugi dla drugiej. Jest również zależny od kontekstu. Na przykład, jeśli mam ustawioną flagę, znaczenia mogą być wyraźniejsze przy jednej składni nad drugą:

if NOT ( fileHandleStatus == FS_OPEN )

w przeciwieństwie do

if ( fileHandleStatus != FS_OPEN )
DopeGhoti
źródło
I nawet if ( FS_OPEN != fileHandleStatus )w wyniku łatwości przypadkowego pisania =zamiast ==w językach, w których pierwszym jest przypisanie, a później test równości (jak C) ...
derobert