Vous êtes sur la page 1sur 8

19/10/2017 Boucles

Tuteurs informatique
Nous contacter

Utiliser ce site
Actu et stages
Structures de contrôle
Docs à imprimer
Plan du site
Rechercher Examiner une condition : if et case
La structure if
Les tuteurs
Unix La structure case
Shell Répéter une procédure : while, until et for
Boucles La boucle while
Commandes La boucle until
Entrée/sortie
La boucle for
Fonctions
Présentation Définir une variable : select
Scripts Code de retour
Tests
Variables Une fois que vous avez compris qu'un script n'est rien d'autre qu'une suite de commandes, vous
avez fait un grand pas ; chaque script consiste à poser des rails : l'utilisateur n'a plus qu'à les suivre (si
Serveur des élèves
ENS
tout cela ne vous paraît pas évident, consultez la page d'initiation à la programmation en shell).
SPI Toutefois, avoir posé les rails ne suffit pas toujours : il peut importer de disposer de mécanismes
d'aiguillage. Les structures de contrôle vous permettront de réaliser ces mécanismes d'aiguillage, qui
donneront à vos scripts souplesse et efficacité.

Examiner une condition : if et case


La structure if
Description

Robert le jardinier programme un hachoir en shell, et veut en interdire l'accès à tout autre
utilisateur que lui-même, afin de protéger le petit Émile de ce dangereux instrument. Le programme
hachoir se pose la question suivante : est-ce que $USER vaut « Robert » ?

Si oui, alors le hachoir doit se mettre en marche ;


si non, alors le hachoir doit refuser de se mettre en marche.

C'est pour les structures de type « si... alors... ou sinon », la commande if a été faite. Elle est
constituée de cinq termes, dont trois sont obligatoires :

1. if (si), qui marque la condition à remplir ;


2. then (alors), qui marque les conséquences si la condition est remplie ;
3. elif (contraction de else if, qui signifie « sinon, si... »), qui est facultatif et marque une
condition alternative ;
4. else (sinon), qui est facultatif et marque le comportement à adopter si la condition n'est pas
remplie ;
5. fi (if à l'envers), qui marque la fin de la structure de branchement.

On peut donc taper :


if commande ; then commandes ; else commandes ; fi

ou bien (car le point et virgule équivaut à un saut de ligne) :


if condition
then commandes
elif condition
then commandes
else commandes
fi

Exemples

Exemple 1

http://www.tuteurs.ens.fr/unix/shell/boucle.html 1/8
19/10/2017 Boucles

Par exemple, pour le hachoir de Robert le jardinier, on aura :


if test $USER = Robert
then echo "Le hachoir va se mettre en marche."
mettre en marche le hachoir
else echo "Quand tu seras grand, $USER."
echo "Et fais bien attention en traversant la rue."
fi

Observations

Comme vous pouvez le constater, à la suite des commandes then et else, vous pouvez mettre
autant de commandes que vous le voulez. Le shell exécute tout ce qu'il trouve jusqu'à la prochaine
instruction.
Par exemple, si la condition indiquée par if est remplie, le shell va exécuter tout ce qui suit then
jusqu'à ce qu'il trouve l'une de ces instructions :

elif,
else ou
fi.

Exemple 2

Autre exemple :
#!/bin/sh
# Fichier "mon-pwd"

if test $PWD = $HOME


then echo "Vous êtes dans votre répertoire d'accueil."
elif test $PWD = $HOME/bin
then echo "Vous êtes dans votre répertoire d'exécutables."
elif test $PWD = $HOME/bin/solaris
then echo "Vous êtes dans votre répertoire d'exécutables pour Solaris."
else echo 'Vous êtes dans un répertoire quelconque, qui n'est ni $HOME,'
echo 'ni $HOME/bin, ni $HOME/bin/solaris. '"Vous êtes dans $PWD."
fi

Observations

1. Le nombre de elif successifs est illimité, alors qu'il ne peut (et c'est logique) y avoir qu'une
seule condition if, une seule instruction else et une seule instruction fi correspondant au if
initial.
2. Remarquez les dernières lignes du script : $HOME est cité dans des guillemets simples, alors que
$PWD est cité entre guillemets doubles. Par conséquent, $HOME ne sera pas interprété (le mot
« $HOME » apparaîtra tel quel), tandis que $PWD sera interprété (il sera remplacé, par exemple,
par /home/toto).

La structure case
Quand préférer case à if ?

La structure de contrôle if est très pratique, mais devient rapidement illisible lorsqu'un aiguillage
offre plusieurs sorties, et que l'on doit répéter une condition pusieurs fois sous des formes à peine
différentes. Typiquement, un programme comme celui-ci est pénible à écrire, à lire et à débuguer :
if test $PWD = $HOME
then echo "Vous êtes dans votre répertoire d'accueil."
elif test $PWD = $HOME/bin
then echo "Vous êtes dans votre répertoire d'exécutables."
elif test $PWD = $HOME/bin/solaris
then echo "Vous êtes dans votre répertoire d'exécutables pour Solaris."
elif test $PWD = $HOME/prive
then echo "Vous êtes dans votre répertoire privé."
else echo 'Vous êtes dans un répertoire quelconque, qui n'est ni $HOME,'
echo 'ni $HOME/bin, ni $HOME/bin/solaris. '"Vous êtes dans $PWD."
fi

Pourquoi ce programme est-il pénible à écrire, à lire et à débuguer ?


http://www.tuteurs.ens.fr/unix/shell/boucle.html 2/8
19/10/2017 Boucles

1. parce qu'on a du mal, en le survolant simplement, à saisir immédiatement son enjeu ;


2. parce qu'il y a une redondance de la séquence « test $PWD = » ; et qui dit redondance dit
possibilité de faute de frappe – erreur particulièrement difficile à repérer à l'œil nu ;
3. parce que les différentes conditions ne sont pas mises sur le même plan (au if correspond un fi
mais les elif n'ont pas besoin d'être ainsi « refermés », alors que logiquement les deux
instructions le sont), ce qui nuit à la lisibilité.

La structure de contrôle case entend remédier à ces inconvénients. Il s'agit tout simplement de la
simplification d'un usage fréquent de la structure if. On peut récrire le programme suivant avec case :
case "$PWD" in
"$HOME") echo "Vous êtes dans votre répertoire d'accueil.";;
"$HOME/bin") echo "Vous êtes dans votre répertoire d'exécutables.";;
"$HOME/bin/solaris") echo "Vous êtes dans votre répertoire d'exécutables pour Solaris.";;
"$HOME/prive") echo "Vous êtes dans votre répertoire privé.";;
*) echo 'Vous êtes dans un répertoire quelconque, qui n'est ni $HOME,'
echo 'ni $HOME/bin, ni $HOME/bin/solaris. '"Vous êtes dans $PWD.";;
esac

Le gain de lisibilité est flagrant (si si, c'est flagrant, je vous assure) :

1. la structure et l'enjeu sont transparents ;


2. la variable que l'on teste n'est citée qu'une seule fois, au lieu d'être répétée à chaque instruction
elif ;
3. les valeurs possibles sont citées à la suite, et toutes sur le même plan.

Syntaxe de case

La structure case adopte la syntaxe suivante :


case chaîne in
motif) commandes ;;
motif) commandes ;;
esac

La structure case commence par évaluer la chaîne. Ensuite, elle va lire chaque motif. Enfin, et dès
qu'une chaîne correspond au motif, elle exécute les commandes appropriées. En anglais, case signifie
« cas » ; cette structure de contrôle permet en effet de réagir adéquatement aux différents « cas de
figure » possibles.
Vous observerez que :

le motif est séparé des commandes par une parenthèse fermante (qui n'est ouverte nulle part !) ;
la série des commandes est close par deux points et virgules successifs (« ;; ») ;
la structure case est close par l'instruction esac, qui n'est autre que le mot case à l'envers (de
même que la structure if est terminée par l'instruction fi).

Un motif (pattern) est un mot contenant éventuellement les constructions *, ?, [a-d], avec la
même signification que pour les raccourcis dans les noms de fichiers (les jokers). Exemple :
case $var in
[0-9]*) echo "$var est un nombre.";;
[a-zA-Z]*) echo "$var est un mot.";;
*) echo "$var n'est ni un nombre ni un mot.";;
esac

L'aiguillage commun à plusieurs motifs

Que faire si je veux aiguiller plusieurs motifs différents de la même façon ? Le premier réflexe est
de répéter, presque à l'identique, des lignes, de la façon suivante :
#!/bin/sh
# Fichier "canards"

echo "Quel est ton nom ?"


read nom

case $nom in
"Riri") echo "Ton oncle s'appelle Donald, Riri.";;
"Fifi") echo "Ton oncle s'appelle Donald, Fifi.";;
"Loulou") echo "Ton oncle s'appelle Donald, Loulou.";;

http://www.tuteurs.ens.fr/unix/shell/boucle.html 3/8
19/10/2017 Boucles
*) echo "Tu n'es pas un neveu de Donald.";;
esac

Mais il n'est jamais bon de répéter manuellement des lignes de programmation, car c'est
générateur de fautes. Il faut toujours chercher des raccourcis, et ne se répéter qu'en dernier recours. On
utilisera donc le caractère « | », qui permet de mettre sur le même plan différents motifs. On pourra
donc écrire :
#!/bin/sh
# Fichier "canards"

echo "Quel est ton nom ?"


read nom

case $nom in
"Riri"|"Fifi"|"Loulou") echo "Ton oncle s'appelle Donald, $nom.";;
*) echo "Tu n'es pas un neveu de Donald.";;
esac

On gagne ainsi en concision, et on évite bien des fautes d'inattention, qui sont particulièrement
coriaces à débuguer.

La structure case exécute les commandes


correspondant au premier motif correct, et ne parcourt
pas les motifs suivants si elle en a trouvé un. Par
conséquent, si le dernier motif est « * », cela revient à
l'instruction else dans la structure if.

Répéter une procédure : while, until et for


Les structures if et case sont des structures d'aiguillage ; les structures while, until et for sont
des boucles. C'est-à-dire qu'elles soumettent à une condition déterminée la répétition (ou non) d'une
suite de commandes.

La boucle while
La boucle while exécute les commandes de manière répétée tant que la première commande
réussit ; en anglais, while signifie « tant que ».

Syntaxe de while

La boucle while adopte la syntaxe suivante :


while condition
do commandes
done

Après l'instruction do, vous pouvez mettre autant de commandes que vous le désirez, suivies
de retours chariot ou bien de points et virgules (« ; »). Le shell exécutera tout ce qu'il trouve, jusqu'à ce
qu'il tombe sur l'instruction done.

Exemple

echo "Tapez le mot de passe :"


read password

while test "$password" != mot de passe correct


do echo "Mot de passe incorrect."
echo "Tapez le mot de passe"
read password
done

echo "Mot de passe correct."


echo "Bienvenue sur les ordinateurs secrets du Pentagone."

Les lignes qui suivent la boucle while seront exécutées si, et seulement si, la condition est remplie,
c'est-à-dire si l'utilisateur tape le bon mot de passe. (En pratique, il est très fortement déconseillé de

http://www.tuteurs.ens.fr/unix/shell/boucle.html 4/8
19/10/2017 Boucles

taper un mot de passe en clair dans un script, car rien n'est plus facile que de l'éditer !...).

Un usage concret de while

Supposons que vous vouliez créer cinquante fichiers sous la forme fichier1, fichier2, fichier3,
etc. Un petit script vous fera ça plus vite que cinquante lignes de commande.
#!/bin/sh
# Fichier "50fichiers"

numero=1

while test $numero != 51


do
# la commande "touch" permet de créer un fichier vide :
touch fichier"$numero"

# cette commande ajoute 1 à la variable "numero" :


numero=$(($numero + 1))
done

Avec ce script, la variable numero est d'abord créée avec la valeur 1. Ensuite, le shell examine si la
valeur de numero est différente de 51 ; c'est le cas. Par conséquent, le shell exécute les commandes
suivantes : il crée un fichier fichier1, puis ajoute 1 à la variable numero, qui vaut donc 2. Et c'est
reparti pour un tour ! Le shell examine si la valeur de numero, c'est-à-dire 2, est différente de 51, etc.
Petit conseil pour faire les choses le plus proprement possible : dans le script qui précède,
remplacez « 51 » par une variable : le script sera beaucoup plus lisible. Exemple :
#!/bin/sh
# Fichier "50fichiers"

numero=1
limite=51

while test $numero != $limite


do
# la commande "touch" permet de créer un fichier vide :
touch fichier"$numero"

# cette commande ajoute 1 à la variable "numero" :


numero=$(($numero + 1))
done

La boucle until
Until signifie « jusqu'à ce que » en anglais. Cette boucle est le pendant de la boucle while, à ceci
près que la condition ne détermine pas la cause de la répétition de la boucle, mais celle de son
interruption.

Syntaxe de until

until condition
do commandes
done

Exemple

On aura ainsi :
echo "Tapez le mot de passe :"
read password

until test $password = mot de passe correct


do echo "Mot de passe incorrect."
echo "Tapez le mot de passe"
read password
done

echo "Mot de passe correct."


echo "Bienvenue sur les ordinateurs secrets de la NASA."

http://www.tuteurs.ens.fr/unix/shell/boucle.html 5/8
19/10/2017 Boucles

Il revient en effet au même de répéter une suite de commandes « tant que le mot de passe est
incorrect » ou bien « jusqu'à ce que le mot de passe soit correct », car un mot de passe déterminé ne
peut être que correct ou incorrect.

while ou until ?

Vous allez donc me demander que préférer entre while et until, si les deux sont si souvent
équivalentes ? Eh bien, celle dont la condition sera la plus facile à écrire, à lire et à débuguer. Si,
comme dans notre exemple, elles sont aussi simples l'une que l'autre, choisissez votre préférée ou
prenez-en une au hasard.

La boucle for
La boucle for affecte successivement à une variable chaque chaîne de caractères trouvée dans une
liste de chaînes, et exécute les commandes une fois pour chaque chaîne.

Syntaxe de for

for var in liste de chaînes


do commandes
done

Exemple

#!/bin/sh
# Fichier "liste"

for element in *
do echo "$element"
done

affiche les noms de tous les fichiers du répertoire courant, un par ligne.
Concrètement, le shell va prendre la liste des chaînes, en l'occurrence « * », c'est-à-dire tous les
fichiers et répertoires du répertoire courant. À chacun de ces éléments, il va attribuer la variable
element, puis exécuter les commandes spécifiées, en l'occurrence echo "$element". Ainsi, si dans mon
répertoire j'ai les fichiers a, b et c, le shell va exécuter successivement echo "a", echo "b" et echo "c".

Usage de for

Comme while et until, for est une boucle au sens propre. Mais il y a une différence notable entre
ces deux groupes de boucle : while et until sont plus appropriés à la répétition à l'identique d'une
séquence de commandes, tandis que for convient particulièrement aux répétitions soumises à de
légères variations.
Prenons par exemple le script qui permettait, avec while, de créer 50 fichiers rapidement. La
numération des passages par la boucle était assez pénible, et éclatée en trois endroits :

1. avant la boucle : l'affectation de la valeur 1 à la variable numero (et, éventuellement, de la


valeur 51 à la variable limite) ;
2. au début de la boucle, la condition test $numero != 51 ou test $numero != $limite ;
3. à la fin de la boucle, l'incrémentation (c'est-à-dire l'augmentation régulière) de la variable
numero, avec la ligne numero=$(($numero + 1)).

Pourquoi dire en trois fois ce qui, dans les langages naturels (c'est-à-dire humains), s'énonce en
une seule : « répéter n fois cette séquence de commandes » ? La boucle for se révèle alors
indispensable :
#!/bin/sh
# Fichier "50fichiers-for"

# la commande "seq a b" compte de a à b


for numero in `seq 1 50`
do touch fichier"$numero"
done

Avouez que le script est beaucoup plus simple de cette manière !

http://www.tuteurs.ens.fr/unix/shell/boucle.html 6/8
19/10/2017 Boucles

Définir une variable : select


Il est parfois utile de donner à l'utilisateur du script des formulaires sous forme de questions à
choix multiples.
Par exemple, je suis sociologue et je veux dresser des statistiques sur les opinions d'un échantillon
représentatif de normaliens : sont-ils favorables ou défavorables à la destruction du NIR (Nouvel
Immeuble Rataud) et à la reconstruction du VIR (Vieil Immeuble Rataud, a.k.a. Pavillon) ?
Spontanément, je fais le script suivant :
#!/bin/sh
# Fichier "vote-nir"

echo "Êtes-vous favorable au remplacement du NIR par le VIR ?"


echo "(Répondez « pour » ou « contre »)"
read opinion

# M'envoyer le résultat par mail


echo "$opinion" | mail bourdieu

Seulement voilà, de nombreuses personnes vont faire des fautes de frappe, et taper « poru » au
lieu de « pour », « cnotre » au lieu de « contre », etc. Résultat : au lieu de laisser d'autres scripts traiter
automatiquement les résultats des statistiques qui me sont envoyés par courriel, je devrai tout regarder
à la main et cocher moi-même les résultats pour ne pas en oublier. Dans ce cas, autant faire un sondage
classique à la sortie du Pot !
Heureusement, il existe une parade : la boucle select.

Syntaxe de select

select variable in valeurs possibles


do commandes
done

Questions à choix multiples

Voici le script de notre sociologue :


#!/bin/sh
# Fichier "vote-nir"

echo "Êtes-vous favorable au remplacement du NIR par le VIR ?"


select opinion in Pour Contre
do break
done

# M'envoyer le résultat par mail


echo "$opinion" | mail bourdieu

Les utilisateurs verront alors :


Êtes-vous favorable au remplacement du NIR par le VIR ?
1) Pour
2) Contre

Ils devront alors taper 1 ou 2, selon leur opinion.


Vous avez remarqué que l'instruction do est suivie de la commande break. Celle-ci indique de
sortir de la boucle select. Sans break, select est en effet une boucle, comme while, until et for. Nous
allons voir pourquoi.

Utiliser select comme boucle

Par défaut, select se comporte comme une boucle. Observons le comportement du script
suivant :
#!/bin/sh
# Fichier "piege"

echo "Si tu arrives à sortir de ce script, je te donne 30 euros."


select issue in "Sortir" "Quitter"
# la commande "continue" est une commande vide : elle ne fait rien de spécial

http://www.tuteurs.ens.fr/unix/shell/boucle.html 7/8
19/10/2017 Boucles
do continue
done

Essayez maintenant d'exécuter ce script :


Si tu arrives à sortir de ce script, je te donne 30 euros.
1) Sortir
2) Quitter
#? 1
#? 2
#? 1
#? 2
#? 1
(etc.)

Ce comportement est parfois utile, par exemple si vous programmez un petit jeu, ou autre. Ou
encore, pour prévenir les réponses non valides. Exemple :
#!/bin/sh
# Fichier "vote-nir"

echo "Êtes-vous favorable au remplacement du NIR par le VIR ?"


select opinion in Pour Contre
do
case $opinion in
# Laisser passer ceux qui répondent correctement à la question
"Pour"|"Contre") break;;

# Au cas où des zozos tapent sur autre chose que 1 ou 2


*) continue;;
esac
done

# M'envoyer le résultat par mail


echo "$opinion" | mail bourdieu

Grâce à cela, si un normalien tape une réponse invalide, select attend qu'il tape quelque chose de
correct ; s'il tape quelque chose de correct, la réponse est envoyée au sociologue.

Vous aurez remarqué au passage que l'on peut


sans problème imbriquer des structures de contrôle
les unes dans les autres.

Code de retour
On remarque que la condition des commandes if et while est une commande. Chaque commande
renvoie un code de retour (qui est ignoré en utilisation normale). Si le code est 0, la commande a
réussi ; sinon, la commande a échoué. Par exemple, le compilateur cc renvoie un code d'erreur non nul
si le fichier compilé contient des erreurs, ou s'il n'existe pas.
Les commandes if et while considèrent donc le code de retour 0 comme « vrai », et tout autre
code comme « faux ».
Il existe une commande test, qui évalue des expressions booléennes (c'est-à-dire dont la valeur
ne peut être que vraie ou fausse) passées en argument, et renvoie un code de retour en fonction du
résultat. Elle est bien utile pour les scripts. Exemple :
if test $var = foo
then echo 'La variable vaut foo'
else echo 'La variable ne vaut pas foo'
fi

Pour en savoir plus sur les opérateurs de test, consultez la page tests et calculs arithmétiques Ou
bien vous pouvez revenir à la page centrale sur le shell, d'où vous pourrez vous orienter vers d'autres
parties du cours.

Auteur : Baptiste Mélès. Dernière modification : 2016-08-14 par Antoine Amarilli.

http://www.tuteurs.ens.fr/unix/shell/boucle.html 8/8