Opérateurs d'égalité Bash (==, -eq)

136

Quelqu'un peut-il expliquer la différence entre -eqet ==dans les scripts bash?

Y a-t-il une différence entre ce qui suit?

[ $a -eq $b ] et [ $a == $b ]

Est-ce simplement que cela ==n'est utilisé que lorsque les variables contiennent des nombres?

Victor Brunell
la source

Réponses:

187

C'est l'inverse: =et ==sont pour les comparaisons de chaînes, -eqest pour les comparaisons numériques. -eqest dans la même famille que -lt, -le, -gt, -geet -ne, si cela vous aide même qui est qui.

==est un bash-isme, au fait. Il vaut mieux utiliser le POSIX =. En bash, les deux sont équivalents, et en clair, sh =est le seul qui fonctionne.

$ a=foo
$ [ "$a" = foo ]; echo "$?"       # POSIX sh
0
$ [ "$a" == foo ]; echo "$?"      # bash specific
0
$ [ "$a" -eq foo ]; echo "$?"     # wrong
-bash: [: foo: integer expression expected
2

(Note latérale: citez ces extensions de variables! Ne laissez pas les doubles guillemets ci-dessus.)

Si vous écrivez un #!/bin/bashscript, je vous recommande d' utiliser à la [[place . La forme doublée a plus de fonctionnalités, une syntaxe plus naturelle et moins de pièges qui vous feront trébucher. Les guillemets doubles ne sont plus nécessaires autour $a, pour un:

$ [[ $a == foo ]]; echo "$?"      # bash specific
0

Voir également:

John Kugelman
la source
1
@DJCrashdummy, [[dans son ensemble, est un ksh-ism des années 1980 que bash (et de nombreux autres shells) a adopté. C'est toute la question - si vous avez [[ du tout , alors vous pouvez supposer que toutes les extensions KSH mises en œuvre autour d' elle ( fnmatch()correspondance de motif de style, ERE expressions régulières avec =~, et oui, la suppression de la chaîne de division et système de fichiers globbing) seront disponible. Comme [[la syntaxe n'est pas POSIX dans son intégralité, il n'y a pas de perte de portabilité supplémentaire en supposant que les fonctionnalités avec lesquelles elle est née seront disponibles.
Charles Duffy le
1
@DJCrashdummy, ... le lien que vous montrez indique correctement que les guillemets sont parfois nécessaires sur le côté droit de [[ $var = $pattern ]], si vous voulez que ce qui serait autrement interprété comme un modèle fnmatch soit plutôt interprété comme une chaîne littérale. La chaîne foon'a pas d'interprétation non littérale, ce qui rend les guillemets totalement sûrs; c'est seulement si l'OP voulait correspondre, disons, foo*(avec l'astérisque comme littéral, ne signifiant rien qui puisse venir après la chaîne foo) que des guillemets ou des échappements seraient nécessaires.
Charles Duffy
@CharlesDuffy, malheureusement, je n'ai aucune idée de ce à quoi vous faites référence, car mon commentaire semble être supprimé et je ne m'en souviens plus car cela fait pas mal de temps. :-(
DJCrashdummy
@DJCrashdummy, ... hein, j'ai supposé que vous l'aviez retiré vous-même. Fondamentalement, c'était une objection au fait que les extensions non citées à l'intérieur [[étaient un bashisme (indiquant que c'était la seule raison pour laquelle vous ne donniez pas la réponse un +1).
Charles Duffy le
@CharlesDuffy non, ce n'est pas la seule raison: à part le fait que je n'aime pas les bash-isms, les ksh-isms etc. pour les tâches simples (cela cause juste des problèmes et déroute les débutants), je ne comprends pas pourquoi mélanger single accolades [ ... ]et double signe égal ==. : - /
DJCrashdummy
27

Cela dépend de la construction de test autour de l'opérateur. Vos options sont les doubles parenthèses, les doubles accolades, les accolades simples ou le test

Si vous utilisez ((...)) , vous testez l'équité arithmétique avec ==comme en C:

$ (( 1==1 )); echo $?
0
$ (( 1==2 )); echo $?
1

(Remarque: 0signifie trueau sens Unix et non nul est un test échoué)

L'utilisation -eqde la double parenthèse est une erreur de syntaxe.

Si vous utilisez [...] (ou une accolade simple) ou [[...]] (ou une double accolade), ou testvous pouvez utiliser l'une des options -eq, -ne, -lt, -le, -gt ou -ge comme comparaison arithmétique .

$ [ 1 -eq 1 ]; echo $?
0
$ [ 1 -eq 2 ]; echo $?
1
$ test 1 -eq 1; echo $?
0

L' ==intérieur des accolades simples ou doubles (ou testcommande) est l'un des opérateurs de comparaison de chaînes :

$ [[ "abc" == "abc" ]]; echo $?
0
$ [[ "abc" == "ABC" ]]; echo $?
1

En tant qu'opérateur de chaîne, =équivaut à ==et notez l'espace blanc autour =ou ==son obligatoire.

Bien que vous puissiez le faire [[ 1 == 1 ]]ou [[ $(( 1+1 )) == 2 ]]il teste l'égalité des chaînes - pas l'égalité arithmétique.

Donc -eqproduit le résultat probablement attendu que la valeur entière de 1+1est égale à2 même si le RH est une chaîne et a un espace de fin:

$ [[ $(( 1+1 )) -eq  "2 " ]]; echo $?
0

Alors qu'une comparaison de chaînes de la même sélection prend l'espace de fin et donc la comparaison de chaînes échoue:

$ [[ $(( 1+1 )) ==  "2 " ]]; echo $?
1

Et une comparaison de chaînes erronée peut produire la mauvaise réponse complète. «10» est inférieur lexicographiquement à «2», donc une comparaison de chaînes renvoie trueou 0. Tant de gens sont mordus par ce bug:

$ [[ 10 < 2 ]]; echo $?
0

vs le test correct pour 10 étant arithmétiquement inférieur à 2:

$ [[ 10 -lt 2 ]]; echo $?
1

Dans les commentaires, il y a une question de la raison technique en utilisant l'entier -eqsur les chaînes renvoie True pour les chaînes qui ne sont pas les mêmes:

$ [[ "yes" -eq "no" ]]; echo $?
0

La raison en est que Bash n'est pas typé . Le -eqfait que les chaînes soient interprétées comme des entiers si possible, y compris la conversion de base:

$ [[ "0x10" -eq 16 ]]; echo $?
0
$ [[ "010" -eq 8 ]]; echo $?
0
$ [[ "100" -eq 100 ]]; echo $?
0

Et 0si Bash pense que ce n'est qu'une chaîne:

$ [[ "yes" -eq 0 ]]; echo $?
0
$ [[ "yes" -eq 1 ]]; echo $?
1

Donc [[ "yes" -eq "no" ]]équivaut à[[ 0 -eq 0 ]]

Dernière note: de nombreuses extensions spécifiques à Bash des constructions de test ne sont pas POSIX et échoueront donc dans d'autres shells. D'autres coques ne supportent généralement pas [[...]]et ((...))ou== .

dawg
la source
Je suis curieux de connaître la raison technique du [[ "yes" -eq "no" ]]retour de True. Comment bash contraint-il ces chaînes à des valeurs entières pouvant être comparées? ;-)
odony
4
Les variables Bash ne sont pas typées, donc [[ "yes" -eq "no" ]]équivaut à [[ "yes" -eq 0 ]] ou [[ "yes" -eq "any_noninteger_string" ]]- All True. La -eqcomparaison d'entiers force. Le "yes"est interprété comme un entier 0; la comparaison est True si l'autre entier est l'un 0ou l' autre ou si le résultat de la chaîne l'est 0.
dawg
Boo, sifflement re: affichage (non portable) ==dans les échantillons de code et ne mentionnant que (portable, standardisé) en =dessous.
Charles Duffy
24

==est un alias spécifique à bash pour =et il effectue une comparaison de chaîne (lexicale) au lieu d'une comparaison numérique. eqétant une comparaison numérique bien sûr.

Enfin, je préfère généralement utiliser le formulaire if [ "$a" == "$b" ]

Elliott Frisch
la source
15
Utiliser ==ici est une mauvaise forme, comme seul le =spécifie POSIX.
Charles Duffy
9
Si vous insistez vraiment pour l'utiliser, ==mettez-le entre [[et ]]. (Et assurez-vous que la première ligne de votre script spécifie à utiliser /bin/bash.)
holgero
18

Guys: Plusieurs réponses montrent des exemples dangereux. L'exemple d'OP [ $a == $b ]utilisait spécifiquement la substitution de variables sans guillemets (à partir de l'édition d'octobre 2017). Car [...]c'est sûr pour l'égalité des chaînes.

Mais si vous allez énumérer des alternatives comme [[...]], vous devez également informer que le côté droit doit être cité. S'il n'est pas cité, c'est une correspondance de modèle! (De la page de manuel de bash: "N'importe quelle partie du modèle peut être citée pour forcer sa correspondance sous forme de chaîne.").

Ici, dans bash, les deux instructions donnant "yes" sont des correspondances de modèle, les trois autres sont l'égalité des chaînes:

$ rht="A*"
$ lft="AB"
$ [ $lft = $rht ] && echo yes
$ [ $lft == $rht ] && echo yes
$ [[ $lft = $rht ]] && echo yes
yes
$ [[ $lft == $rht ]] && echo yes
yes
$ [[ $lft == "$rht" ]] && echo yes
$
bk-se
la source
2
Doit être [ "$lht" = "$rht" ] avec les guillemets pour être fiable même pour l'égalité. Si vous avez créé un fichier avec touch 'Afoo -o AB', [ $lft = $rht ]retournera true, même si ce nom de fichier n'est pas du tout identique à AB.
Charles Duffy