La syntaxe de la matière non égale?

22

Lors de l'écriture de scripts, j'écris généralement mes ifs avec la syntaxe suivante car il est plus facile pour moi de comprendre que ce qui vient ensuite n'est pas vrai.

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

D'autres disent que le chemin ci-dessous est meilleur

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

Le problème, c'est quand je demande pourquoi et s'il y a des différences, personne ne semble avoir de réponse.

Alors, y a-t-il des différences entre les deux syntaxes? L'un est-il plus sûr que l'autre? Ou s'agit-il simplement d'une question de préférence / d'habitude?

Jimmy_A
la source
9
Dans le premier cas , vous devez savoir les opérateurs de priorité de distinguer !(x==y)de (!x)==y.
jimmij
@jimmij Voulez-vous dire que lorsque vous comparez une chaîne, cela se if [ ! "$string" = "one" ]traduit par sinon la valeur de $stringégal one. Et cela se if [ "$string" != "one"]traduit par si la valeur de $stringn'est pas égale à one?
Jimmy_A
5
@Jimmy_A Je veux dire que vous devez savoir comment cela "se traduit". Rassembler les opérateurs ( !=syntaxe) est juste plus évident.
jimmij
Chers électeurs proches, la réponse de Stéphane montre qu'il existe une différence matérielle de comportement, la bonne réponse n'est en aucun cas fondée sur l'opinion.
Kevin

Réponses:

35

Outre les arguments cosmétiques / préférence, une des raisons pourrait être qu'il y a plus d'implémentations où [ ! "$a" = "$b" ]échoue dans les cas d'angle qu'avec [ "$a" != "$b" ].

Les deux cas devraient être sûrs si les implémentations suivent l'algorithme POSIX , mais même aujourd'hui (début 2018 au moment de l'écriture), il y a toujours des implémentations qui échouent. Par exemple, avec a='(' b=')':

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

Avec les dashversions antérieures à 0.5.9, comme la 0.5.8 trouvée shsur Ubuntu 16.04 par exemple:

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

(corrigé dans 0.5.9, voir https://www.mail-archive.com/[email protected]/msg00911.html )

Ces implémentations traitent [ ! "(" = ")" ]comme [ ! "(" "text" ")" ]c'est [ ! "text" ](test si « texte » est la chaîne null) alors que les mandats POSIX à être [ ! "x" = "y" ](test « x » et « y » pour l' égalité). Ces implémentations échouent car elles effectuent le mauvais test dans ce cas.

Notez qu'il existe encore un autre formulaire:

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

Celui-ci nécessite un shell POSIX (ne fonctionnera pas avec l'ancien shell Bourne).

Notez que plusieurs implémentations ont également eu des problèmes avec [ "$a" = "$b" ](et [ "$a" != "$b" ]) et font toujours comme le [buildin de /bin/shSolaris 10 (un shell Bourne, le shell POSIX étant dedans /usr/xpg4/bin/sh). Voilà pourquoi vous voyez des choses comme:

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

Dans des scripts essayant d'être portables sur d'anciens systèmes.

Stéphane Chazelas
la source
En d'autres termes, les deux syntaxes font la même chose, sans aucune différence, mais avec ! "$a" = "b"vous devez être plus prudent sur la façon dont vous l'écrivez pour spécifier la comparaison. D'après votre exemple, je comprends que la deuxième commande revient, not zeroce qui pourrait être à la fois bénéfique ou troublant. Cela pourrait être bénéfique si vous voulez quitter si elles ne correspondent pas, ou troublant si vous voulez voir si la comparaison s'est écrasée
Jimmy_A
2
@Jimmy_A, non, dans ces implémentations [ ! "$a" = "$b" ]échouent simplement quand $aest (et $best ), il prétend qu'elles sont identiques quand elles ne le sont pas, (n'est pas la même chaîne que ), mais [effectue le mauvais test dans ce cas.
Stéphane Chazelas
Ahhh, je pense que je l'ai. Les premier et troisième signifient de vérifier si la condition est vraie, tandis que les deuxième et quatrième si la condition est fausse. Ainsi, les codes d'état de sortie étant différents. Il s'agit essentiellement de dire que 1 et 4 variables sont différentes et 2 et 3 différentes.
Jimmy_A
3
@Jimmy_A, non. Vous avez complètement raté le point. Si ces implémentations suivaient POSIX, alors tous les codes d'état seraient 0.
Wildcard
Pour être sûr de bien comprendre, cela se résume à: [ ! "$a" = "$b" ]est parfois géré de manière incorrecte dans les implémentations de shell buggy (ce qui, je pense, reprend plus ou moins la première phrase de la réponse).
Michael Burr
8

La x != ysyntaxe est meilleure car elle ! x == yest sujette aux erreurs - nécessite une connaissance de la priorité des opérateurs qui diffère d'une langue à l'autre. La syntaxe ! x == ypourrait être interprétée comme !(x == y)ou (!x) == y, en fonction de la priorité de !vs =.


Par exemple, la c++négation ! précède l'opérateur de comparaison / relationnel == , d'où le code suivant:

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

résultats

true
false
true
false

Un comportement similaire peut être observé dans de nombreuses autres langues, y compris par exemple awk- un outil fréquemment utilisé dans le monde Unix.


En revanche, le regroupement des opérateurs via x != yne conduit à aucune confusion en tant que modèle bien établi. De plus, techniquement parlant, il !=s'agit très souvent non pas de deux, mais d'un seul opérateur, ce qui devrait être encore légèrement plus rapide à évaluer qu'une comparaison séparée puis une négation. Par conséquent, bien que les deux syntaxes fonctionnent en bash, je recommanderais de suivre x != ycar il est beaucoup plus facile de lire et de maintenir du code qui suit une logique standard.

jimmij
la source
4

Ce genre de chose est très basé sur l'opinion, car la "réponse" dépend très fortement de la façon dont le cerveau d'un individu est câblé. S'il est vrai que sémantiquement, NOT ( A == B )est identique à (A != B ), l'un pourrait être plus clair pour une personne et l'autre pour une autre. Il dépend également du contexte. Par exemple, si j'ai un indicateur défini, les significations pourraient être plus claires avec une syntaxe par rapport à une autre:

if NOT ( fileHandleStatus == FS_OPEN )

par opposition à

if ( fileHandleStatus != FS_OPEN )
DopeGhoti
la source
Et même if ( FS_OPEN != fileHandleStatus )en raison de la facilité de taper accidentellement =plutôt que ==dans les langues où le premier est assigné et le test d'égalité ultérieur (comme C) ...
derobert