Les scripts shell - Instructions de contrôle
Lorsqu’elles se terminent, toutes les commandes exécutées par le shell renvoient un code de retour (également appelé code de statut ou de sortie).
Les tests
-
Si la commande s’est correctement exécutée, la convention veut que le code de statut ait pour valeur zéro.
-
Si la commande a rencontré un problème lors de son exécution, son code de statut aura une valeur différente de zéro.
Les raisons peuvent être nombreuses : manque de droits d’accès, absence de fichier, saisie incorrecte, etc.
Il faut se référer au manuel de la commande man commande
pour connaître les différentes valeurs du code de retour prévues par les développeurs.
Le code de retour n’est pas visible directement, mais est enregistré dans une variable spéciale : $?
.
$ mkdir repertoire
$ echo $?
0
$ mkdir /repertoire
mkdir: impossible de créer le répertoire
$ echo $?
1
$ commande_qui_n_existe_pas
commande_qui_n_existe_pas : commande introuvable
$ echo $?
127
L’affichage du contenu de la variable |
Puisque la valeur de
|
Il est également possible de créer des codes de retour dans un script.
Il suffit pour cela d’ajouter un argument numérique à la commande exit
.
$ bash # pour éviter d'être déconnecté après le « exit 2 »
$ exit 2
$ echo $?
2
Outre la bonne exécution d’une commande, le shell offre la possibilité d’exécuter des tests sur de nombreux motifs :
-
Fichiers : existence, type, droits, comparaison ;
-
Chaînes de caractères : longueur, comparaison ;
-
Numériques entiers : valeur, comparaison.
Le résultat du test :
-
$?=0
: le test s’est correctement exécuté et est vrai ; -
$?=1
: le test s’est correctement exécuté et est faux ; -
$?=2
: le test ne s’est pas correctement exécuté.
Tester le type d’un fichier
test [-d|-e|-f|-L] fichier
Option | Observation |
---|---|
|
Teste si le fichier existe |
|
Teste si le fichier existe et est de type normal |
|
Teste si le fichier existe et est de type répertoire |
|
Teste si le fichier existe et est de type lien symbolique |
|
Teste si le fichier existe et est de type spécial mode bloc |
|
Teste si le fichier existe et est de type spécial mode caractère |
|
Teste si le fichier existe et est de type tube |
|
Teste si le fichier existe et est de type socket |
|
Teste si le fichier existe et est de type terminal |
|
Teste si le fichier existe et est accessible en lecture |
|
Teste si le fichier existe et est accessible en écriture |
|
Teste si le fichier existe et est est exécutable |
|
Teste si le fichier existe et est a un SGID positionné |
|
Teste si le fichier existe et est a un SUID positionné |
|
Teste si le fichier existe et est non vide (taille > 0 octets) |
Comparer deux fichiers
La commande test
peut également comparer des fichiers :
test fichier1 [-nt|-ot|-ef] fichier2
Option | Observation |
---|---|
|
Teste si le premier fichier est plus récent que le second |
|
Teste si le premier fichier est plus ancien que le second |
|
Teste si le premier fichier est un lien physique du second |
Tester une variable
test [-z|-n] $variable
Option | Observation |
---|---|
|
Teste si la variable est vide |
|
Teste si la variable n’est pas vide |
Tester une chaîne de caractères
test chaîne1 [=|!=] chaîne2
Exemple :
$ test "$var" = "Hello world !"
$ echo $?
0
Option | Observation |
---|---|
|
Teste si la première chaîne de caractères est égale à la seconde |
|
Teste si la première chaîne de caractères est différente de la seconde |
|
Teste si la première chaîne de caractères est avant la seconde dans l’ordre ASCII |
|
Teste si la première chaîne de caractères est après la seconde dans l’ordre ASCII |
Comparaison de numériques entiers
test "num1" [-eq|-ne|-gt|-lt] "num2"
Exemple :
$ var=1
$ test "$var" -eq "1"
$ echo $?
0
$ var=2
$ test "$var" -eq "1"
$ echo $?
1
Option | Observation |
---|---|
|
Teste si le premier nombre est égal au second |
|
Teste si le premier nombre est différent au second |
|
Teste si le premier nombre est supérieur au second |
|
Teste si le premier nombre est inférieur au second |
Les numériques étant traités par le shell comme des caractères (ou chaînes de caractères) classiques, un test sur un caractère peut renvoyer le même résultat qu’il soit traité en tant que numérique ou non.
Mais le résultat du test n’aura pas la même signification :
|
Combinaison de tests
La combinaison de tests permet d’effectuer plusieurs tests en une seule commande. Il est possible de tester plusieurs fois le même argument (fichier, chaîne ou numérique) ou des arguments différents.
test option1 argument1 [-a|-o] option2 argument 2
$ ls -lad /etc
drwxr-xr-x 142 root root 12288 sept. 20 09:25 /etc
$ test -d /etc -a -x /etc
$ echo $?
0
Option | Observation |
---|---|
|
ET : Le test sera vrai si tous les motifs le sont. |
|
OU : Le test sera vrai si au moins un motif l’est. |
Les tests peuvent ainsi être groupé avec des parenthèses ( )
pour leur donner une priorité.
(TEST1 -a TEST2) -a TEST3
Le caractère !
permet d’effectuer le test inverse de celui demandé par l’option :
$ test -e /fichier # vrai si fichier existe
$ ! test -e /fichier # vrai si fichier n'existe pas
Les opérations numériques
La commande expr
effectue une opération avec des entiers numériques.
expr num1 [+] [-] [\*] [/] [%] num2
Exemple :
$ expr 2 + 2
4
Attention à bien encadrer le signe d’opération par une espace, vous obtiendrez un message d’erreur en cas d’oubli. |
Opérateur | Observation |
---|---|
|
Addition |
|
Soustraction |
|
Multiplication |
|
Quotient de la division |
|
Modulo de la division |
La commande typeset
La commande typeset -i
déclare une variable comme un entier.
Exemple :
$ typeset -i var1
$ var1=1+1
$ var2=1+1
$ echo $var1
2
$ echo $var2
1+1
La commande let
La commande let
teste si un caractère est numérique.
Exemple :
$ var1="10"
$ var2="AA"
$ let $var1
$ echo $?
0
$ let $var2
$ echo $?
1
La commande
|
La commande let
permet également d’effectuer des opérations mathématiques :
$ let var=5+5
$ echo $var
10
|
Structures conditionnelles
Si la variable $?
permet de connaître le résultat d’un test ou de l’exécution d’une commande, elle ne peut qu’être affichée et n’a aucune incidence sur le déroulement d’un script.
Mais nous pouvons nous en servir dans une condition. Si le test est bon alors je fais cette action sinon je fais telle autre action.
if commande
then
commande si $?=0
else
commande si $?!=0
fi
La commande placée après le mot if
peut être n’importe quelle commande puisque c’est son code de retour ($?
) qui sera évalué.
Il est souvent pratique d’utiliser la commande test
pour définir plusieurs actions en fonction du résultat de ce test (fichier existe, variable non vide, droits en écriture positionnés).
Utiliser une commande classique (mkdir
, tar
, …) permet de définir les actions à effectuer en cas de succès ou les messages d’erreur à afficher en cas d’échec.
if test -e /etc/passwd
then
echo "Le fichier existe"
else
echo "Le fichier n'existe pas"
fi
if mkdir rep
then
cd rep
fi
La commande Ainsi :
peut devenir :
|
Si le bloc else
commence par une nouvelle structure if
, il est possible de fusionner else
et if
:
[…]
else
if test -e /etc/
[…]
[…]
# est équivalent à
elif test -e /etc
[…]
La structure if
/ then
/ else
/ fi
évalue la commande placée après if
:
-
Si le code retour de cette commande est 0 (vrai) le shell exécutera les commandes placées après
then
; -
Si le code retour est différent de 0 (faux) le shell exécutera les commandes placées après
else
.
Le bloc else
est facultatif.
Il existe un besoin d’effectuer certaines actions uniquement si l’évaluation de la commande est vraie et n’avoir rien à faire si elle est fausse.
Le mot fi
ferme la structure.
Lorsqu’il n’y a qu’une seule commande à exécuter dans le bloc then
, il est possible d’utiliser une syntaxe plus simple.
La commande à exécuter si $?
est vrai est placée après &&
tandis que la commande à exécuter si $?
est faux est placée après ||
(facultatif).
Par exemple :
$ test -e /etc/passwd && echo "Le fichier existe" || echo "Le fichier n'existe pas"
$ mkdir repert && echo "Le répertoire est créé"
Il est possible d’évaluer et de remplacer une variable avec une structure plus légère que if
.
Cette syntaxe met en œuvre les accolades :
-
Affiche une valeur de remplacement si la variable est vide :
${variable:-valeur}
-
Affiche une valeur de remplacement si la variable n’est pas vide :
${variable:+valeur}
-
Affecte une nouvelle valeur à la variable si elle est vide :
${variable:=valeur}
Exemples :
$ nom=""
$ echo ${nom:-linux}
linux
$ echo $nom
$ echo ${nom:=linux}
linux
$ echo $nom
linux
$ echo ${nom:+tux}
tux
$ echo $nom
linux
Structure alternative conditionnelle case
Une succession de structures if
peut vite devenir lourde et complexe.
Lorsqu’elle concerne l’évaluation d’une même variable, il est possible d’utiliser une structure conditionnelle à plusieurs branches.
Les valeurs de la variable peuvent être précisées ou appartenir à une liste de possibilités.
Les caractères jokers sont utilisables.
La structure case … in
/ esac
évalue la variable placée après case
et la compare aux valeurs définies.
À la première égalité trouvée, les commandes placées entre )
et ;;
sont exécutées.
La variable évaluée et les valeurs proposées peuvent être des chaînes de caractères ou des résultats de sous-exécutions de commandes.
Placé en fin de structure, le choix *
indique les actions à exécuter pour toutes les valeurs qui n’ont pas été précédemment testées.
case $variable in
valeur1)
commandes si $variable = valeur1
;;
valeur2)
commandes si $variable = valeur2
;;
[..]
*)
commandes pour toutes les valeurs de $variable != de valeur1 et valeur2
;;
esac
Lorsque la valeur est sujette à variation, il est conseillé d’utiliser les caractères jokers []
pour spécifier les possibilités :
[Oo][Uu][Ii])
echo "oui"
;;
Le caractère |
permet aussi de spécifier une valeur ou une autre :
"oui" | "OUI")
echo "oui"
;;
Boucles
Le shell bash permet l’utilisation de boucles. Ces structures permettent l’exécution d’un bloc de commandes plusieurs fois (de 0 à l’infini) selon une valeur définie statiquement, dynamiquement ou sur condition :
-
while
-
until
-
for
-
select
Quelle que soit la boucle utilisée, les commandes à répéter se placent entre les mots do
et done
.
La structure boucle conditionnelle while
La structure while
/ do
/ done
évalue la commande placée après while
.
Si cette commande est vrai ($? = 0)
, les commandes placées entre do
et done
sont exécutées.
Le script retourne ensuite au début évaluer de nouveau la commande.
Lorsque la commande évaluée est fausse ($? != 0)
, le shell reprend l’exécution du script à la première commande après done
.
while commande
do
commande si $? = 0
done
Exemple :
while test -e /etc/passwd
do
echo "Le fichier existe"
done
Si la commande évaluée ne varie pas, la boucle sera infinie et le shell n’exécutera jamais les commandes placées à la suite du script. Cela peut être volontaire, mais aussi être une erreur. Il faut donc faire très attention à la commande qui régit la boucle et trouver un moyen d’en sortir. |
Pour sortir d’une boucle while
, il faut faire en sorte que la commande évaluée ne soit plus vraie, ce qui n’est pas toujours possible.
Il existe des commandes qui permettent de modifier le comportement d’une boucle :
-
exit
-
break
-
continue
La commande exit
La commande exit
termine l’exécution du script.
exit [n]
Exemple :
$ bash # pour éviter d'être déconnecté après le « exit 1 »
$ exit 1
$ echo $?
1
La commande exit
met fin au script immédiatement.
Il est possible de préciser le code de retour du script en le précisant en argument (de 0 à 255).
Sans argument précisé, c’est le code de retour de la dernière commande du script qui sera transmise à la variable $?
.
Cette commande est utile dans le cas d’un menu proposant la sortie du script dans les choix possibles.
La commande break
/ continue
La commande break
permet d’interrompre la boucle en allant à la première commande après done
.
La commande continue
permet de relancer la boucle en revenant à la première commande après do
.
while test -d /
do
echo "Voulez-vous continuer ? (oui/non)"
read rep
test $rep = "oui" && continue
test $rep = "non" && break
done
Les commandes true
/ false
La commande true
renvoie toujours vrai tandis que la commande false
renvoie toujours faux.
$ true
$ echo $?
0
$ false
$ echo $?
1
Utilisées comme condition d’une boucle, elles permettent soit d’exécuter une boucle infinie soit de désactiver cette boucle.
Exemple :
while true
do
echo "Voulez-vous continuer ? (oui/non)"
read rep
test $rep = "oui" && continue
test $rep = "non" && break
done
La structure boucle conditionnelle until
La structure until
/ do
/ done
évalue la commande placée après until
.
Si cette commande est fausse ($? != 0)
, les commandes placées entre do
et done
sont exécutées.
Le script retourne ensuite au début évaluer de nouveau la commande.
Lorsque la commande évaluée est vraie ($? = 0)
, le shell reprend l’exécution du script à la première commande après done
.
until commande
do
commande si $? != 0
done
Exemple :
until test -e /etc/passwd
do
echo "Le fichier n'existe pas"
done
La structure choix alternatif select
La structure select
/ do
/ done
permet d’afficher rapidement un menu avec plusieurs choix et une demande de saisie.
À chaque élément de la liste correspond un choix numéroté.
À la saisie, la valeur choisie est affectée à la variable placée après select
(créée à cette occasion).
Elle exécute ensuite les commandes placées entre do
et done
avec cette valeur.
-
La variable
PS3
contient le message d’invitation à entrer le choix ; -
La variable
REPLY
va permettre de récupérer le numéro du choix.
Il faut une commande break
pour sortir de la boucle.
La structure |
PS3="Votre choix :"
select variable in var1 var2 var3
do
commandes
done
Exemple :
PS3="Votre choix : "
select choix in café thé chocolat
do
echo "Vous avez choisi le $REPLY : $choix"
done
ce qui donne à l’exécution :
1) Café
2) Thé
3) Chocolat
Votre choix : 2
Vous avez choisi le choix 2 : thé
Votre choix :
La structure boucle sur liste de valeurs for
La structure for
/ do
/ done
affecte le premier élément de la liste à la variable placée après for
(créée à cette occasion).
Elle exécute ensuite les commandes placées entre do
et done
avec cette valeur.
Le script retourne ensuite au début affecter l’élément suivant de la liste à la variable de travail.
Lorsque le dernier élément a été utilisé, le shell reprend l’exécution à la première commande après done
.
for variable in liste
do
commandes
done
Exemple :
for fichier in /home /etc/passwd /root/fic.txt
do
file $fichier
done
Toute commande produisant une liste de valeurs peut être placée à la suite du in
à l’aide d’une sous-exécution.
-
Avec la variable
IFS
contenant$' \t\n'
, la bouclefor
prendra chaque mot du résultat de cette commande comme liste d’éléments sur laquelle boucler . -
Avec la variable
IFS
contenant$'\t\n'
(c’est-à-dire sans espace), la bouclefor
prendra chaque ligne du résultat de cette commande.
Cela peut être les fichiers d’un répertoire. Dans ce cas, la variable prendra comme valeur chacun des mots des noms des fichiers présents :
for fichier in $(ls -d /tmp/*)
do
echo $fichier
done
Cela peut être un fichier. Dans ce cas, la variable prendra comme valeur chaque mot contenu dans le fichier parcouru, du début à la fin :
$ cat mon_fichier.txt
première ligne
seconde ligne
troisième ligne
$ for LIGNE in $(cat mon_fichier.txt); do echo $LIGNE; done
première
ligne
seconde
ligne
troisième
ligne
Pour lire ligne par ligne un fichier, il faut modifier la valeur de la variable d’environnement IFS
.
$ IFS=$'\t\n'
$ for LIGNE in $(cat mon_fichier.txt); do echo $LIGNE; done
première ligne
seconde ligne
troisième ligne
Tester vos connaissances
Toute commande renvoie obligatoirement un code de retour à la fin de son exécution :
Vrai
Faux
Un code de retour à 0
indique une erreur d’exécution :
Vrai
Faux
Le code de retour est stocké dans la variable $@
:
Vrai
Faux
La commande test
permet de :
Tester le type d’un fichier
Tester une variable
Comparer des numériques
Comparer le contenu de 2 fichier
La commande expr
:
Concatène 2 chaînes de caractères
Effectue des opérations mathématiques
Affiche du texte à l’écran
La syntaxe de la structure conditionnelle ci-dessous vous semble-t’elle correcte ? Expliquez pourquoi.
Vrai
Faux
if commande
commande si $?=0
else
commande si $?!=0
fi
Que sigifie la syntaxe suivante : ${variable:=valeur}
Affiche une valeur de remplacement si la variable est vide
Affiche une valeur de remplacement si la variable n’est pas vide
Affecte une nouvelle valeur à la variable si elle est vide
La syntaxe de la structure alternative conditionnelle ci-dessous vous semble-t’elle correcte ? Expliquez pourquoi.
Vrai
Faux
case $variable in
valeur1)
commandes si $variable = valeur1
valeur2)
commandes si $variable = valeur2
*)
commandes pour toutes les valeurs de $variable != de valeur1 et valeur2
;;
esac
Parmi les propositions ci-dessous, laquelle n’est pas une structure pour faire une boucle :
while
until
loop
for
La commande true
renvoit toujours 1
:
Vrai
Faux