26. Constructeurs de listes

La syntaxe en liste ET et liste OU fournit un moyen d'exécuter plusieurs commandes l'une après l'autre. Elle peut remplacer efficacement des constructions complexes if/then imbriquées, et même des instructions case.

Chaîner des commandes

liste ET
commande-1 && commande-2 && commande-3
          && ... commande-n

Chaque commande s'exécute à son tour, à condition que la dernière ait renvoyé un code de retour true (zéro). Au premier retour false (différent de zéro), la chaîne de commandes s'arrête (la première commande renvoyant false est la dernière à être exécutée).

Exemple 26.1. Utiliser une liste ET pour tester des arguments de la ligne de commande

#!/bin/bash
# "liste ET"

if [ ! -z "$1" ] && echo "Argument #1 = $1" && [ ! -z "$2" ] \
&& echo "Argument #2 = $2"
then
  echo "Au moins deux arguments passés au script."
  # Toute la commande chaînée doit être vraie.
else
  echo "Moins de deux arguments passés au script."
  # Au moins une des commandes de la chaîne a renvoyé faux.
fi  
# Notez que "if [ ! -z $1 ]" fonctionne mais que son supposé équivalent,
#  if [ -n $1 ] ne fonctionne pas.
#    Néanmoins, mettre entre guillemets corrige cela :
#  if [ -n "$1" ] fonctionne.
#    Attention !
# Il est toujours mieux de mettre entre guillemets les variables testées.


# Ceci accomplit la même chose en utilisant une instruction if/then pure.
if [ ! -z "$1" ]
then
  echo "Argument #1 = $1"
fi
if [ ! -z "$2" ]
then
  echo "Argument #2 = $2"
  echo "Au moins deux arguments passés au script."
else
  echo "Moins de deux arguments passés au script."
fi
# C'est plus long et moins élégant que d'utiliser une "liste ET".


exit 0

Exemple 26.2. Un autre test des arguments de la ligne de commande en utilisant une liste and

#!/bin/bash

ARGS=1            # Nombre d'arguments attendus.
E_MAUVAISARGS=65  # Valeur de sortie si un nombre incorrect d'arguments est passé.

test $# -ne $ARGS && \
echo "Usage: `basename $0` $ARGS argument(s)" && exit $E_MAUVAISARGS
#  Si la condition 1 est vraie (mauvais nombre d'arguments passés au script),
#+ alors le reste de la ligne s'exécute et le script se termine.

# La ligne ci-dessous s'exécute seulement si le test ci-dessus a échoué.
echo "Bon nombre d'arguments passés à ce script."

exit 0

# Pour vérifier la valeur de sortie, faites un "echo $?" après la fin du script.

Bien sûr, une liste ET peut aussi initialiser des variables à une valeur par défaut.

arg1=$@ && [ -z "$arg1" ] && arg1=DEFAULT
                
              # Initialise $arg1 avec les arguments en ligne de commande.
              # Mais... Initialise à DEFAUT si non spécifié sur la ligne de commande.
liste OR
commande-1 || commande-2 || commande-3 || ...
commande-n

Chaque commande s'exécute à son tour aussi longtemps que la commande précédente renvoie false. Au premier retour true, la chaîne de commandes s'arrête (la première commande renvoyant true est la dernière à être exécutée). C'est évidemment l'inverse de la « liste ET ».

Exemple 26.3. Emploi de listes OU en combinaison avec une liste ET

#!/bin/bash

#  delete.sh, utilitaire pas-si-stupide de suppression de fichier.
#  Usage : delete nomfichier

E_MAUVAISARGS=65

if [ -z "$1" ]
then
  echo "Usage : `basename $0` nomfichier"
  exit $E_MAUVAISARGS  # Pas d'argument ? On sort.
else  
  fichier=$1           # Initialisation du nom du fichier.
fi  


[ ! -f "$fichier" ] && echo "Le fichier \"$fichier\" introuvable. \
Je refuse peureusement d'effacer un fichier inexistant."
# LISTE ET, pour donner le message d'erreur si le fichier est absent.
# Notez que le message echo continue sur la seconde ligne avec un échappement.

[ ! -f "$file" ] || (rm -f $file; echo "Fichier \"$file\" supprimé.")
# LISTE OU, pour supprimer le fichier si présent.

# Notez la logique inversée ci-dessus.
# La LISTE-ET s'exécute si vrai, la LISTE-OU si faux.

exit 0

[Attention]

Attention

Si la première commande dans une liste OU renvoie true, elle sera exécutée.

# ==> Les astuces suivantes proviennent du
#+==> script /etc/rc.d/init.d/single de Miquel van Smoorenburg
#+==> Illustre l'utilisation des listes "ET" et "OU".
# ==> Les commentaires "à flèche" ont été ajoutés par l'auteur de ce document.

[ -x /usr/bin/clear ] && /usr/bin/clear
  # ==> Si /usr/bin/clear existe, alors il est exécuté
  # ==> Vérifier l'existence d'une commande avant de l'utiliser
  #+==> évite des messages d'erreur et d'autres conséquences bizarres.

  # ==> . . .

# S'ils veulent lancer quelque chose en mode simple utilisateur, autant le
# lancer...
for i in /etc/rc1.d/S[0-9][0-9]* ; do
        # Vérifier si le script est ici.
        [ -x "$i" ] || continue
  # ==> Si le fichier correspondant n'est *pas* trouvé dans $PWD,
  #+==> alors "continue"z en sautant au début de la boucle.

        # Rejete les fichiers de sauvegarde et les fichiers générés par rpm.
        case "$1" in
                *.rpmsave|*.rpmorig|*.rpmnew|*~|*.orig)
                        continue;;
        esac
        [ "$i" = "/etc/rc1.d/S00single" ] && continue
  # ==> Initialise le nom du script, mais ne l'exécute pas encore.
        $i start
done

  # ==> . . .
[Important]

Important

Le code de sortie d'une liste ET ou d'une liste OU correspond au code de sortie de la dernière commande exécutée.

Des combinaisons intelligentes de listes ET et OU sont possibles, mais la logique peut rapidement devenir difficile et nécessiter une grande vigilance sur les règles de précédence des opérateurs, et aussi, peut-être, des phases de débogage intensives.

false && true || echo false    # false

# Même résultat avec
( false && true ) || echo false     # false
# Mais PAS avec
false && ( true || echo false )     # (rien ne s'affiche)

# Notez le groupement de gauche à droite et une évaluation des instructions
# car les opérateurs logiques "&&" et "||" ont la même priorité.

# Il est en général préférable d'éviter ce genre de constructions complexes.

# Merci, S.C.

Voir l'Exemple A.7, « days-between : Calculer le nombre de jours entre deux dates » et l'Exemple 7.4, « Test de liens cassés » pour des illustrations de l'utilisation de listes ET / OU pour tester des variables.