La programmation «Shell»


précédentsommairesuivant

IX. Les structures de contrôles

IX-A. Introduction

Comme tout langage évolué, le Shell permet des structures de contrôles. Ces structures sont :

  • l'alternative simple (« &&… », « ||… ») ;
  • l'alternative complexe (« if… ») ;
  • le branchement sur cas multiples (« case… ») ;
  • la boucle (« while… », « until… », « for… »).

IX-B. L'alternative simple

Syntaxe

 
Sélectionnez
cde1 && cde2 
cde1 || cde2

La première syntaxe correspond à un « commande1 ET commande2 » et se traduit par « exécuter la commande n° 1 ET (sous-entendu « si celle-ci est « vrai » donc s'est exécutée entièrement ») exécuter la commande n° 2 ».

La seconde syntaxe correspond à un « commande1 OU commande2 » et se traduit par « exécuter la commande n° 1 OU (sous-entendu « si celle-ci est « faux » donc ne s'est pas exécutée entièrement ») exécuter la commande n° 2 ».

Exemple

Écrire un script affichant si on lui a passé zéro, un ou plusieurs paramètres. Ensuite il devra afficher les paramètres reçus.

 
Sélectionnez
#!/bin/sh 
# Script affichant si on lui passe zéro, un ou plusieurs paramètres 
# Ensuite il affiche ces paramètres 

# Test sur aucun paramètre 
test $# -eq 0 && echo "$0 n'a reçu aucun paramètre" 

# Test sur un paramètre 
test $# -eq 1 && echo "$0 a reçu un paramètre qui est $1" 

# Test sur plusieurs paramètres 
test $# -gt 1 && echo "$0 a reçu $# paramètres qui sont $*"

Remarque

Il est possible d'enchaîner les alternatives par la syntaxe « cde1 && cde2 || cde3 ». L'inconvénient de cette syntaxe est qu'on ne peut placer qu'une commande en exécution de l'alternative, ou alors, si on désire placer plusieurs commandes, on est obligé de les grouper avec des parenthèses.

IX-C. L'alternative complexe

Syntaxe

 
Sélectionnez
if liste de commandes 
then 
    commande1 
    [ commande2 ?] 
[ else 
    commande3 
    [ commande4 ?] ] 
fi

La structure « if…then…[else]…fi » évalue toutes les commandes placées après le « if », mais ne vérifie que le code de retour de la dernière commande de la liste. Dans le cas où le programmeur voudrait placer plusieurs commandes dans la « liste de commandes », il doit les séparer par le caractère « point‑virgule » qui est un séparateur de commandes Shell. Dans la pratique, cette possibilité est très rarement utilisée, un script étant plus lisible si les commandes non vérifiées par le « if » sont placées avant celui‑ci.

Si l'état de la dernière commande est « vrai », le Shell ira exécuter l'ensemble des instructions placées après dans le bloc « then », sinon, il ira exécuter l'ensemble des instructions placées dans le bloc « else » si celui‑ci existe.

Dans tous les cas, le Shell ira exécuter les instructions éventuellement placées derrière le mot‑clef « fi », car celui‑ci termine le « if ».

Il est possible d'imbriquer plusieurs blocs « if…fi » à condition de placer un mot‑clef « fi » pour chaque mot‑clef « if ».

On peut se permettre de raccourcir une sous‑condition « else if… » par le mot‑clef « elif ». Dans ce cas, il n'y a plus qu'un seul « fi » correspondant au « if » initial.

Exemple avec des « if imbriqués »

 
Sélectionnez
#!/bin/sh 
echo "Entrez un nombre" 
read nb 
if test $nb -eq 0 # if n°1 
then 
    echo "C'était zéro" 
else 
    if test $nb -eq 1 # if n°2 
    then 
        echo "C'était un" 
    else 
        echo "Autre chose" 
    fi # fi n°2 
fi # fi n°1

Exemple avec des « elif »

 
Sélectionnez
#!/bin/sh 
echo "Entrez un nombre" 
read nb 
if test $nb -eq 0 # if n°1 
then 
    echo "C'était zéro" 
elif test $nb -eq 1 # Sinon si 
then 
    echo "C'était un" 
else 
    echo "Autre chose" 
fi # fi n°1

IX-D. Le branchement à choix multiples

Syntaxe

 
Sélectionnez
case chaine in 
    val1) 
        commande1 
        [ commande2 ?] 
        ;; 
    [val2) 
        commande3 
        [ commande4 ?] 
        ;;] 
esac

La structure « case…esac » évalue la chaîne en fonction des différents choix proposés. À la première valeur trouvée, les instructions correspondantes sont exécutées.

Le double « point‑virgule » indique que le bloc correspondant à la valeur testée se termine. Il est donc obligatoire… sauf si ce bloc est le dernier à être évalué.

La chaîne et/ou les valeurs de choix peuvent être construites à partir de variables ou de sous‑exécutions de commandes. De plus, les valeurs de choix peuvent utiliser les constructions suivantes :

Construction Signification
[x-y] La valeur correspond à tout caractère compris entre « x » et « y »
[xy] La valeur testée correspond à « x » ou « y »
xx|yy La valeur correspond à deux caractères « xx » ou « yy »
? La valeur testée correspond à un caractère quelconque
* La valeur testée correspond à toute chaîne de caractères (cas « autres cas »)

Exemple

Script qui fait saisir un nombre et qui évalue ensuite s'il est pair, impair, compris entre 10 et 100 ou autre chose.

 
Sélectionnez
#!/bin/sh 
# Script de saisie et d'évaluation simple du nombre saisi 

# Saisie du nombre 
echo "Entrez un nombre" 
read nb 

# Évaluation du nombre 
case $nb in 
    0) echo "$nb vaut zéro";; 
    1|3|5|7|9) echo "$nb est impair";; 
    2|4|6|8) echo "$nb est pair";; 
    [1-9][0-9]) echo "$nb est supérieur ou égal à 10 et inférieur à 100";; 
    *) echo "$nb est un nombre trop grand pour être évalué" 
esac

IX-E. La boucle sur conditions

Syntaxe

 
Sélectionnez
while liste de commandes 
do 
    commande1 
    [ commande2 ?] 
done 
until liste de commandes 
do 
    commande1 
    [ commande2 ?] 
done

La boucle « while do…done » exécute une séquence de commandes tant que la dernière commande de la « liste de commandes » est « vrai » (statut égal à zéro).

La boucle « until do…done » exécute une séquence de commandes tant que la dernière commande de la « liste de commandes » est « faux » (statut différent de zéro).

Exemple

Script qui affiche tous les fichiers du répertoire courant et qui, pour chaque fichier, indique si c'est un fichier de type « répertoire », de type « ordinaire » ou d'un autre type.

 
Sélectionnez
#!/bin/sh 
# Script d'affichage d'informations sur les fichiers du répertoire courant 

# La commande "read" lit l'entrée standard. Mais cette entrée peut être redirigée d'un pipe 
# De plus, "read" renvoie "vrai" quand elle a lu et "faux" quand il n'y a plus rien à lire 
# On peut donc programmer une boucle de lecture pour traiter un flot d'informations 
ls | while read fic # Tant que le "read" peut lire des infos provenant du "ls" 
do 
    # Évaluation du fichier traité 
    if test -d "$fic" 
    then 
        echo "$fic est un répertoire" 
    elif test -f "$fic" 
    then 
        echo "$fic est un fichier ordinaire" 
    else 
        echo "$fic est un fichier spécial ou lien symbolique ou pipe ou socket" 
    fi 
done

IX-F. La boucle sur liste de valeurs

Syntaxe

 
Sélectionnez
for var in valeur1 [valeur2 ?] 
do 
    commande1 
    [ commande2 ?] 
done

La boucle « for… do…done » va boucler autant de fois qu'il existe de valeurs dans la liste. À chaque tour, la variable « $var » prendra séquentiellement comme contenu la valeur suivante de la liste.

Les valeurs de la liste peuvent être obtenues de différentes façons (variables, sous‑exécutions…). La syntaxe « in valeur1 … » est optionnelle. Dans le cas où elle est omise, les valeurs sont prises dans la variable « $* » contenant les arguments passés au programme.

Dans le cas où une valeur contient un métacaractère de génération de nom de fichier (« étoile », « point d'interrogation »…), le Shell examinera alors les fichiers présents dans le répertoire demandé au moment de l'exécution du script et remplacera le métacaractère par le ou les fichiers dont le nom correspond au métacaractère.

Exemple

Même script que dans l'exemple précédent, qui affiche tous les fichiers du répertoire courant et qui, pour chaque fichier, indique si c'est un fichier de type « répertoire », de type « ordinaire » ou d'un autre type, mais en utilisant une boucle « for ».

 
Sélectionnez
#!/bin/sh 
# Script d'affichage d'informations sur les fichiers du répertoire courant 
for fic in `ls` # Boucle sur chaque fichier affiché par la commande "ls" 
do 
    # Évaluation du fichier traité 
    if test -d "$fic" 
    then 
        echo "$fic est un répertoire" 
    elif test -f "$fic" 
    then 
        echo "$fic est un fichier ordinaire" 
    else 
        echo "$fic est un fichier spécial ou lien symbolique ou pipe ou socket" 
    fi 
done

Remarques

Ce script présente un léger « bogue » dû à l'emploi de la boucle « for ». En effet, le « for » utilise l'espace pour séparer ses éléments les uns des autres. Il s'ensuit que si un fichier possède un espace dans son nom, le « for » séparera ce nom en deux parties qu'il traitera dans deux itérations distinctes et la variable « fic » prendra alors comme valeurs successives les deux parties du nom.

Ce bogue n'existe pas avec l'emploi de la structure « ls | while read fic… », car le « read » lit la valeur jusqu'à la « fin de ligne ».

Par ailleurs, dans le cas de la commande ls ou encore du parcours de fichiers dans un script, il est préférable de privilégier l'utilisation le métacaractère « * » (encore appelé « wildcard »).

Exemple

Reprenons l'exemple précédent :

 
Sélectionnez
#!/bin/sh 
# Script d'affichage d'informations sur les fichiers du répertoire courant 
for fic in *
do 
    # Évaluation du fichier traité 
    if [ -d "$fic" ]
    then 
        echo "$fic est un répertoire" 
    elif [ -f "$fic" ]
    then 
        echo "$fic est un fichier ordinaire" 
    else 
        echo "$fic est un fichier spécial ou lien symbolique ou pipe ou socket" 
    fi 
done

Il est aussi possible de parcourir des valeurs itératives comme dans la plupart des langages de programmation à l'aide de la boucle for. Pour cela, on peut utiliser la commande seq comme suit :

 
Sélectionnez
# parcours et affichage des valeurs allant de 0 à 10
for i in `seq 0 10`
do 
    echo $i 
done

Dans les langages shell dits « évolués », il est également permis d'utiliser ces types de syntaxe dont on retrouve des équivalences dans d'autres langages de programmation courants :

 
Sélectionnez
# parcours et affichage des valeurs allant de 0 à 10
for (( i=0 ; i <= 10 ; i++ ))
do 
    echo $i 
done

# Autre syntaxe possible
for i in {0..10}
do
    echo $i
done

IX-G. Interruption d'une ou plusieurs boucles

Syntaxe

 
Sélectionnez
break [n] 
continue [n]

L'instruction « break [n] » va faire sortir le programme de la boucle numéro « n » (« 1 » par défaut). L'instruction passera directement après le « done » correspondant à cette boucle.

L'instruction « continue [n] » va faire repasser le programme à l'itération suivante de la boucle numéro « n » (« 1 » par défaut). Dans le cas d'une boucle « while » ou « until », le programme repassera à l'évaluation de la condition. Dans le cas d'une boucle « for », le programme passera à la valeur suivante.

La numérotation des boucles s'effectue à partir de la boucle la plus proche de l'instruction « break » ou « continue », qu'on numérote « 1 ». Chaque boucle englobant la précédente se voit affecter un numéro incrémental (2, 3…). Le programmeur peut choisir de sauter directement sur la boucle numérotée « n » en mettant la valeur « n » derrière l'instruction « break » ou « continue ».

Remarques

  • L'utilisation de ces instructions est contraire à la philosophie de la « programmation structurée ». Il incombe donc à chaque programmeur de toujours réfléchir au bien-fondé de leurs mises en application.
  • Contrairement aux croyances populaires, la structure « if… fi » n'est pas une boucle.

Exemple

Script qui fait saisir un nom et un âge. Mais il contrôle que l'âge soit celui d'un majeur et soit valide (entre 18 et 200 ans). Ensuite, il inscrit ces informations dans un fichier. La saisie s'arrête sur un nom vide où un âge à « 0 ».

 
Sélectionnez
#!/bin/sh 
# Script de saisie; de contrôle et d'enregistrement d'un nom et d'un âge 

while true # Boucle infinie 
do 
    # Saisie du nom et sortie sur nom vide 
    echo "Entrez un nom : "; read nom 
    test -z "$nom" && break # Sortie de la boucle infinie si nom vide 

    # Saisie et contrôle de l'âge 
    while true # Saisie en boucle infinie 
    do 
        echo "Entrez un âge : "; read age 
        test $age -eq 0 && break 2 # Sortie de la boucle infinie si age = 0 
        test $age -ge 18 -a $age -lt 200 && break # Sortie de la boucle de saisie si age correct 
    done 

    # Enregistrement des informations dans un fichier "infos.dat" 
    echo "Nom: $nom; Age: $age" >>infos.dat 
done

IX-H. Interruption d'un programme

Syntaxe

 
Sélectionnez
exit [n]

L'instruction « exit [n] » met immédiatement fin au Shell dans lequel cette instruction est exécutée.

Le paramètre « n » facultatif (qui vaut « 0 » par défaut) ne peut pas dépasser « 255 ». Ce paramètre sera récupéré dans la variable « $? » du processus ayant appelé ce script (processus père). Cette instruction « exit » peut donc rendre un script « vrai » ou « faux » selon les conventions du Shell.

Remarque

Même sans instruction « exit », un script Shell renvoie toujours au processus père un état qui est la valeur de la variable « $? » lorsque le script se termine (état de la dernière commande du script).

IX-I. Le générateur de menus en boucle (Korn Shell et Bourne Again Shell et shells descendants)

Syntaxe

 
Sélectionnez
select var in chaîne1 [chaîne2 ?] 
do 
    commande1 
    [ commande2 ?] 
done

La structure « select… do… done » proposera à l'utilisateur un menu prénuméroté commençant à « 1 ». À chaque numéro sera associé une chaîne prise séquentiellement dans les chaînes de la liste. Il lui sera aussi proposé de saisir un des numéros du menu (le prompt de saisie provenant de la variable « $PS3 »).

Après la saisie, la chaîne correspondant au numéro choisi sera stockée dans la variable « $var » pendant que la valeur du numéro choisi sera stocké dans la variable interne « $REPLY ». Il appartient alors au programmeur d'évaluer correctement l'une de ces deux variables (« if…fi » ou « case…esac ») pour la suite de son programme. Dans le cas où l'utilisateur choisit un numéro qui n'est pas dans la liste, la variable « $var » recevra alors une chaîne vide, mais le numéro choisi sera quand même stocké dans la variable « $REPLY ». Cependant, la variable de statut « $? » n'est pas modifiée par ce choix erroné.

Comme pour la boucle « for », les valeurs de la liste peuvent être obtenues de différentes façons (variables, sous‑exécutions…). La syntaxe « in chaîne1 … » est optionnelle. Dans le cas où elle est omise, les valeurs sont prises dans la variable « $* » contenant les arguments passés au programme ou à la fonction. Dans le cas où une valeur contient un métacaractère de génération de nom de fichier (« étoile », « point d'interrogation »…), le Shell examinera alors les fichiers présents dans le répertoire de travail au moment de l'exécution du script et remplacera le métacaractère par le ou les fichiers dont le nom correspond au métacaractère.

Remarque

La phase « menu + choix » se déroule en boucle infinie. Il est donc nécessaire de programmer l'interruption de la boucle sur une valeur particulière de la variable « $var » ou de la variable « $REPLY » en utilisant une des instructions « break », « return » ou « exit ».


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+