A sintaxe da matéria não é igual?

22

Ao criar scripts, geralmente escrevo meus ifs com a seguinte sintaxe, pois é mais fácil entender que o que vem a seguir não é verdadeiro.

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

Outros dizem que o caminho abaixo é melhor

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

A questão é quando pergunto por que e se existem diferenças, ninguém parece ter resposta.

Então, existem diferenças entre as duas sintaxes? Um deles é mais seguro que o outro? Ou é apenas uma questão de preferência / hábito?

Jimmy_A
fonte
9
No primeiro caso, você precisa saber operadores prioridade para distinguir !(x==y)a partir (!x)==y.
jimmij
@jimmij Você quer dizer que, ao comparar uma string, isso se if [ ! "$string" = "one" ]traduz em senão o valor de $stringigual one. E isso se if [ "$string" != "one"]traduz se o valor de $stringnão for igual a one?
Jimmy_A 12/01
5
@ Jimmy_A Quero dizer que você precisa saber como isso "se traduz". Reunir operadores ( !=sintaxe) é apenas mais óbvio.
jimmij
Caros eleitores próximos, a resposta de Stéphane mostra que há uma diferença material de comportamento; a resposta correta não é de forma alguma baseada em opiniões.
Kevin Kevin

Respostas:

35

Além dos argumentos cosméticos / de preferência, um motivo pode ser o fato de haver mais implementações em que [ ! "$a" = "$b" ]falhas nos casos de canto do que com [ "$a" != "$b" ].

Ambos os casos devem ser seguros se as implementações seguirem o algoritmo POSIX , mas ainda hoje (início de 2018), ainda existem implementações que falham. Por exemplo, com a='(' b=')':

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

Com dashversões anteriores à 0.5.9, como a 0.5.8 encontrada shno Ubuntu 16.04, por exemplo:

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

(corrigido em 0.5.9, consulte https://www.mail-archive.com/[email protected]/msg00911.html )

Essas implementações tratam [ ! "(" = ")" ]como [ ! "(" "text" ")" ]é [ ! "text" ](teste se "texto" é a cadeia nula), enquanto o POSIX exige que seja [ ! "x" = "y" ](teste "x" e "y" para igualdade). Essas implementações falham porque executam o teste errado nesse caso.

Observe que há ainda outra forma:

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

Esse requer um shell POSIX (não funcionará com o shell Bourne antigo).

Observe que várias implementações também tiveram problemas com [ "$a" = "$b" ](e [ "$a" != "$b" ]) e ainda se assemelham às [do /bin/shSolaris 10 (um shell Bourne, o shell POSIX está dentro /usr/xpg4/bin/sh). É por isso que você vê coisas como:

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

Em scripts, tentando ser portável para sistemas antigos.

Stéphane Chazelas
fonte
Portanto, em outras palavras, ambas as sintaxas fazem o mesmo, sem diferenças, mas ! "$a" = "b"você precisa ter mais cuidado com a forma como a escreve para especificar a comparação. Pelo seu exemplo, entendo que o segundo comando retorna, o not zeroque pode ser benéfico ou preocupante. Poderia ser benéfico se você quiser sair se eles não corresponderem, ou incomodando se você quiser ver se a comparação caiu
Jimmy_A
2
@ Jimmy_A, não, nessas implementações [ ! "$a" = "$b" ]apenas falha quando $aé (e $bé ), alega que elas são idênticas quando não são, (não é a mesma string que ), mas [executa o teste errado nesse caso.
Stéphane Chazelas
Ahhh, acho que entendi. O primeiro e o terceiro significam para verificar se a condição é verdadeira, enquanto o segundo e o quarto se a condição for falsa. Assim, os códigos de status de saída são diferentes. É basicamente dizer que 1 e 4 variáveis ​​são diferentes e 2 e 3 não são iguais.
Jimmy_A 12/01
3
@Jimmy_A, não. Você perdeu completamente o ponto. Se essas implementações seguissem o POSIX, todos os códigos de status seriam 0. #
Wildcard
Para garantir que eu entenda, isso se resume a: [ ! "$a" = "$b" ]às vezes, é manipulado incorretamente em implementações de shell de buggy (que eu acho que está mais ou menos reafirmando a primeira frase da resposta).
Michael Burr
8

A x != ysintaxe é melhor porque ! x == yé propensa a erros - requer conhecimento da precedência dos operadores, que difere de idioma para idioma. A sintaxe ! x == ypoderia ser interpretado como !(x == y)ou (!x) == y, dependendo prioridade !vs =.


Por exemplo, na c++negação ! vem antes do operador de comparação / relacional == , portanto, o seguinte código:

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

retorna

true
false
true
false

Comportamento semelhante pode ser observado em muitas outras línguas, incluindo, por exemplo awk- uma ferramenta frequentemente usada no mundo Unix.


Por outro lado, reunir operadores via via x != ynão leva a nenhuma confusão como um padrão bem estabelecido. Além disso, tecnicamente falando !=muitas vezes não são dois, mas apenas um operador, portanto, deve ser ainda mais marginalmente mais rápido que avaliar do que comparar separadamente e depois negar. Portanto, embora ambas as sintaxes funcionem no bash, eu recomendaria seguir x != y, pois é muito mais fácil ler e manter o código que segue alguma lógica padrão.

jimmij
fonte
4

Esse tipo de coisa é muito baseada em opiniões, pois a "resposta" depende muito da maneira como o cérebro de um indivíduo é conectado. Embora seja verdade que, semanticamente, NOT ( A == B )seja idêntico a (A != B ), um pode ser mais claro para uma pessoa e o outro para outra. Também é dependente do contexto. Por exemplo, se eu tiver um sinalizador definido, os significados podem ficar mais claros com uma sintaxe do que com outra:

if NOT ( fileHandleStatus == FS_OPEN )

em oposição a

if ( fileHandleStatus != FS_OPEN )
DopeGhoti
fonte
E até mesmo if ( FS_OPEN != fileHandleStatus )como resultado da facilidade de acidentalmente digitando =em vez de ==nas línguas em que o primeiro é atribuição eo teste de igualdade mais tarde (como C) ...
derobert