7.3. Autres opérateurs de comparaison

Un opérateur de comparaison binaire compare deux variables ou quantités. Remarque : les comparaisons d'entiers et les comparaisons de chaînes de caractères utilisent un ensemble d'opérateurs différent.

comparaisons d'entiers

-eq

est égal à

if [ "$a" -eq "$b" ]

-ne

n'est pas égal à

if [ "$a" -ne "$b" ]

-gt

est supérieur à

if ["$a" -gt "$b" ]

-ge

est supérieur ou égal à

if [ "$a" -ge "$b" ]

-lt

est inférieur à

if [ "$a" -lt "$b" ]

-le

est inférieur ou égal à

if [ "$a" -le "$b" ]

<

est inférieur à (entre doubles parenthèses)

(("$a" < "$b"))

<=

est inférieur ou égal à (entre doubles parenthèses)

(("$a"< "$b"))

>

est supérieur à (dans une expression entre doubles-parenthèses)

(("$a" > "$b"))

>=

est supérieur ou égal à (dans une expression entre doubles-parenthèses)

(("$a" >= "$b"))

comparaison de chaînes de caractères

=

est égal à

if [ "$a" = "$b" ]

[Attention]

Attention

Notez le blanc qui entoure le signe =.

if [ "$a"="$b" ] ne produit pas le même résultat.

==

est égal à

if [ "$a" == "$b" ]

Synonyme de =.

[Note]

Note

L'opérateur de comparaison == se comporte différemment à l'intérieur d'un test à double crochets et à l'intérieur de crochets simples.

[[ $a == z* ]]    # Vrai si $a commence par 'z'
                  # (correspondance d'expression rationnelle).

[[ $a == "z*" ]]  # Vrai si $a est égal à z* (égalité littérale).

[ $a == z* ]      # La correspondance de fichiers et la coupure de
                  # mots pourront avoir lieu.
[ "$a" == "z*" ]  # Vrai si $a est égal à z* (égalité littérale).

# Merci à Stéphane Chazelas
!=

n'est pas égal à

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

Cet opérateur utilise la reconnaissance de motifs à l'intérieur d'expressions en [[ ... ]].

<

est inférieur à, dans l'ordre alphabétique ASCII

if [[ "$a" < "$b" ]]

if [ "$a" \< "$b" ]

Notez que, s'il se trouve dans une expression en [ ], « < » doit être précédé d'un caractère d'échappement.

>

est supérieur à, dans l'ordre alphabétique ASCII

if [[ "$a" > "$b" ]]

if [ "$a" \> "$b" ]

Notez que, s'il se trouve dans une expression en [ ], « > » doit être précédé d'un caractère d'échappement.

Voir l'Exemple 27.10, « Le tri bulle : Bubble Sort » pour une application de cet opérateur de comparaison.

-z

la chaîne de caractères est vide, c'est-à-dire qu'elle est de longueur nulle

Chaine=''   # Variable chaîne de caractères de longueur nulle (vide).

if [ -z "$Chaine" ]
then
  echo "\$Chaine est vide."
else
  echo "\$Chaine n'est PAS vide."
fi     # $Chaine est vide.
-n

la chaîne de caractères n'est pas vide.

[Attention]

Attention

Attention : Le test -n exige que la chaîne de caractères soit entre guillemets à l'intérieur des crochets de test. Pour une chaîne sans guillemets, il faut utiliser l'option ! -z, ou encore laisser seule la chaîne à tester dans guillemets, (voir l'Exemple 7.6, « Vérifier si une chaîne est vide »), mais ce sont des pratiques risquées. Placez toujours vos chaînes de caractères à tester entre guillemets. [36]

Exemple 7.5. Comparaisons des nombres et des chaînes de caractères

#!/bin/bash

a=4
b=5

#  Ici, "a" et "b" peuvent être soit des entiers, soit des
#+ chaînes de caractères.
#  Il y a un certain flou entre comparaisons arithmétiques et
#+ comparaisons de chaînes car les variables Bash ne sont pas 
#+ fortement typées.

#  Bash permet les opérations prévues pour les entiers, et les
#  comparaisons, sur les variables qui contiennent uniquement des
#  caractères numériques. Néanmoins, il convient d'être prudent.

echo

if [ "$a" -ne "$b" ]
then
  echo "$a n'est pas égal à $b"
  echo "(comparaison arithmétique)"
fi

echo

if [ "$a" != "$b" ]
then
  echo "$a n'est pas égal à $b."
  echo "(comparaison de chaînes de caractères)"
  #     "4"  != "5"
  # ASCII 52 != ASCII 53
fi

# Dans ce cas précis, "-ne" et "!=" fonctionnent tous les deux.

echo

exit 0

Exemple 7.6. Vérifier si une chaîne est vide

#!/bin/bash
# str-test.sh: Tester des chaînes vides sans guillemets,
# "but not strings and sealing wax, not to mention cabbages and kings..."
# (extrait de l'histoire des huîtres, dans "Alice au Pays des Merveilles")

# Utilisation de   if [ ... ]

#  Tant qu'une chaîne n'a pas été initialisée, elle n'a aucune valeur définie.
#  Cet état est appelé "vide" (en anglais "null", ce n'est pas la
#+ même chose que zéro).

if [ -n $chaine1 ]    # $chaine1 n'a été ni déclarée ni initialisée.
then
  echo "La chaîne \"chaine1\" n'est pas vide."
else  
  echo "La chaîne \"chaine1\" est vide."
fi  
# Mauvais résultat.
# Affiche $chaine1 comme non vide alors qu'elle n'a pas été initialisée.


echo


# Nouvel essai.

if [ -n "$chaine1" ]  # Cette fois, $chaine1 est entre guillemets.
then
  echo "La chaîne \"chaine1\" n'est pas vide."
else  
  echo "La chaîne \"chaine1\" est vide."
fi      # Il faut échapper les chaînes à l'intérieur des crochets de test !


echo


if [ $chaine1 ]       # À présent, $chaine1 est toute seule.
then
  echo "La chaîne \"chaine1\" n'est pas vide."
else  
  echo "La chaîne \"chaine1\" est vide."
fi  
# Ça marche.
# L'opérateur de test [ ] tout seul détecte que la chaîne est vide.
# Néanmoins, il est de bonne pratique de mettre des guillemets ("$chaine1").
#
# Comme dirait Stéphane Chazelas :
#    if [ $chaine1 ]    a un seul argument, "]"
#    if [ "$chaine1" ]  a deux arguments, la chaîne vide "$chaine1" et "]" 


echo


chaine1=initialisée

if [ $chaine1 ]       # Cette fois encore, $chaine1 est sans guillemets.
then
  echo "La chaîne \"chaine1\" n'est pas vide."
else  
  echo "La chaîne \"chaine1\" est vide."
fi  
# De nouveau, on a le résultat correct.
# Malgré tout, il vaut mieux mettre "$chaine1" entre guillemets, parce
# que...


chaine1="a = b"

if [ $chaine1 ]       # $chaine1 est encore sans guillemets.
then
  echo "La chaîne \"chaine1\" n'est pas vide."
else  
  echo "La chaîne \"chaine1\" est vide."
fi  
# Ne pas mettre "$chaine1" entre guillemets donne un résultat faux !

exit 0
# Merci également à Florian Wisser pour sa vigilance.

Exemple 7.7. zmore

#!/bin/bash

# Lire les fichiers gzippés avec 'more'

SANSARGS=65
PASTROUVE=66
NONGZIP=67

if [ $# -eq 0 ] # a le même effet que:  if [ -z "$1" ]
# $1 peut exister et être vide, comme dans :  zmore "" arg2 arg3
then
  echo "Usage: `basename $0` nomfichier" >&2
  # Message d'erreur vers stderr.
  exit $SANSARGS
  # Renvoie 65 comme code de sortie du script (code d'erreur).
fi  

nomfichier=$1

if [ ! -f "$nomfichier" ]  #  Mettre $nomfichier entre guillemets permet d'avoir
                           #+ des espaces dans les noms de fichiers.
then
  echo "Fichier $nomfichier introuvable !" >&2
  # Message d'erreur vers stderr.
  exit $PASTROUVE
fi  

if [ ${nomfichier##*.} != "gz" ]
# Utilisation d'accolades pour le développement des expressions"
then
  echo "Le fichier $1 n'est pas une archive gzip !"
  exit $NONGZIP
fi  

zcat $1 | more

# Utilise le filtre 'more'.
# Vous pouvez mettre 'less' à la place, si vous préférez.


exit $?   # Le script renvoie le code d'erreur de la commande tubée.
#  En fait, "exit $?" n'est pas nécessaire, car le script retournera
#+ chaque fois le code de sortie de la dernière commande exécutée.

comparaison composée

-a

et logique

expr1 -a expr2 renvoie vrai si expr1 et expr2 sont vrais tous les deux.

-o

ou logique

expr1 -o expr2 renvoie vrai si soit expr1, soit expr2 est vrai.

Ces options ressemblent beaucoup aux opérateurs de comparaison de Bash && et ||, utilisés à l'intérieur de doubles crochets.

[[ condition1 && condition2 ]]

Les opérateurs -o et -a fonctionnent avec la commande test ou à l'intérieur des crochets simples de test.

if [ "$expr1" -a "$expr2" ]
then
  echo "expr1 and expr2 sont vraies toutes les deux"
else
  echo "expr1 est fausse ou bien expr2 est fausse"
fi
[Attention]

Attention

Cependant, comme le remarque rihad :

[ 1 -eq 1 ] && [ -n "`echo true 1>&2`" ]   # vrai
[ 1 -eq 2 ] && [ -n "`echo true 1>&2`" ]   # (aucune sortie)
# ^^^^^^^ La condition est fausse. Jusqu'ici, tout se passe comme prévu.

# Mais ...
[ 1 -eq 2 -a -n "`echo true 1>&2`" ]       # vrai
# ^^^^^^^ La condition est fausse. Alors, pourquoi cette sortie "true" ?

# Est-ce parce que les deux clauses conditionnelles entre crochets
s'évaluent ?
[[ 1 -eq 2 && -n "`echo true 1>&2`" ]]     # (aucune sortie)
# Non, ce n'est pas cela.

# Apparemment, && et || "court-circuitent" tandis que -a et -o ne le
font pas.

Reportez-vous à l'Exemple 8.3, « Tests de conditions composées avec utilisation de && et de || », à l'Exemple 27.16, « Simuler un tableau à deux dimensions, puis son test » et à l'Exemple A.29, « Chasse aux spammeurs » pour voir à l'oeuvre les opérateurs de comparaison composée.



[36] Comme S.C. l'a relevé, dans un test composé, placer la variable chaîne de caractères entre guillemets peut ne même pas suffire. [ -n "$chaine" -o "$a" = "$b" ] peut provoquer une erreur avec certaines versions de Bash si $chaine est vide. La méthode la plus sûre est d'ajouter un caractère supplémentaire aux variables potentiellement vides, [ "x$chaine" != x -o "x$a" = "x$b" ] (les « x » s'annulent).