Voilà maintenant la substance même du scripting : les commandes, qui ont jusque là été saisies interactivement, vont être regroupées dans un fichier texte et destinées à être exécutées (ou pas !), séquentiellement (ou pas !) par un shell afin d'obtenir un résultat donné (et de préférence toujours le même !) pour une valeur initiale donnée.
Au sein d'un script, les variables du shell telles qu'elles ont été décrites infra permettront de mémoriser des données en cours de traitement ou des états du programme durant son exécution : savoir les manipuler, et comprendre comment le shell les interprète ou les substitue est donc primordial.
Au lancement d'un script, le shell chargé de son exécution va commencer à exécuter séquentiellement chacune des lignes le composant, et après une vérification (lexicale et syntaxique) des éventuelles erreurs qu'il pourrait contenir, procéder à son évaluation (phase d'interprétation et de substitution des variables et des commandes), pour enfin l'exécuter et poursuivre le parcours du fichier.
Contrairement à d'autres langages interprétés, et à fortiori aux langages compilés, le shell ne connait pas d'étape de parsing global du code source à exécuter : les erreurs éventuelles ne seront donc détectées qu'au cas par cas, au fur et à mesure de l'exécution. |
Pour être valide, le nom de toute variable doit obligatoirement débuter par un des caractères compris dans [a-zA-Z_] : une lettre (majuscule ou minuscule) ou un tiret bas. Les caractères suivants pouvant être compris dans l'ensemble [a-zA-Z0-9_] : c'est à dire les mêmes plus les chiffres.
Exemples de noms valides :
_UnNombre |
Un_Nombre |
Un_Nombre_ |
Exemples de noms invalides :
1_Nombre |
-Un_Nombre |
/Un_Nombre_ |
La déclaration d'une variable est optionnelle (elle est alors implicitement déclarée lors de son affectation), mais peut faire l'objet d'une instruction particulière via les commandes internes declare ou typeset qui suivent la syntaxe suivante :
declare | typeset [option] [nom[=affectation]]
Utilisées seules, les commandes declare ou typeset se comportent comme la commande set, et affichent l'ensemble des variables déclarées dans l'environnement courant. Les options permettent notamment de spécifier un type à la variable faisant l'objet de la déclaration : -a indique un tableau, -i un nombre entier, l'option particulière -r marque la variable "en lecture seule" : elle ne peut alors être modifiée, ni supprimée ultérieurement (il s'agira donc d'une constante !).
Par défaut une variable du shell représente une chaine de caractères. |
Exemples de déclarations de variables :
_Unechaine=texte déclare la variable _UneChaine et lui affecte la valeur 'texte'. |
_Unechaine2="texte comportant des espaces" lui affecte la valeur 'texte comportant des espaces'. |
typeset _Unechaine déclare la variable _UneChaine et lui affecte la valeur "chaine vide". |
declare -i Un_Nombre=25 déclare la variable Un_Nombre en tant que valeur numérique et lui affecte la valeur 25. |
La suppression d'une déclaration de variable s'effectue par appel de la commande interne unset [nom].
Exemples de suppressions de variables :
_Unechaine=texte; unset _Unechaine déclare la variable _UneChaine et la supprime aussitôt. |
declare -i -r Un_Nombre=25; unset Un_Nombre la tentative de suppression déclenche une erreur... |
Au cours de son exécution, un shell positionne un certain nombre de variables qui fournissent des informations de différentes natures. La liste exhaustive de ces variables est fournie ci-après :
$# contient le nombre d'arguments reçus par le script en cours d'exécution.
$0 représente le nom du script.
$1, $2, $3 ... $9 les 9 premiers arguments passés au script.
${10}, ${11}, ${12} ... les arguments suivants passés au script (uniquement pour bash et ksh).
$* et $@ contiennent la liste de l'ensemble des arguments passée au script. La différence entre les deux formes si situe au niveau de leur utilisation avec des guillemets : "$*" ne protège pas les éventuels arguments contenant des séparateurs alors que $@ le fait !
$? contient le code de retour d'une commande. Ce code est un entier compris entre 0 et 255, et permet de diagnostiquer la réussite (0) ou l'échec d'une commande unix (>0).
$$ représente le PID du shell en cours d'exécution.
$! représente le PID du dernier processus lancé en arrière plan.
Cette variable particulière contient en fait les caractères utilisés par le shell comme séparateur (par défaut espace, tabulation et retour à la ligne). Elle peut être modifiée en cours de script.
Exemples d'utilisation :
OLDIFS="$IFS" |
IFS=$'\n' |
... |
IFS="$OLDIFS" |
La syntaxe du shell prévoit donc l'utilisation de certains caractères spéciaux qui subiront un traitement particulier lors de l'interprétation d'une ligne de commande ... et c'est la principale raison de la restriction des possibilités de nommage des variables à un jeu particulier de caractères.
Ces caractères spéciaux sont les suivants :
espace tabulation saut de ligne sont les séparateurs de mots dans la ligne de commande (cf. IFS).
; est le séparateur de commandes.
'' "" \ sont les caractères de protection.
& signifie l'exécution en arrière plan.
| < << > >> signifient les tubes et les redirections.
() {} signifient les regroupement de commandes.
* ? [ ] ?() +() *() !() @() sont les caractères de génération de noms de fichiers.
$ ${} permettent d'accéder à la valeur d'une variable.
`` $() signifient la substitution de commandes.
Les ' apostrophes ou simples quotes permettent de retirer la signification de tous les caractères spéciaux du shell ; elles doivent être appariées dans une même ligne de commande, et ne se protègent pas elles-mêmes.
Exemples d'utilisation :
echo $PATH affiche la valeur de la variable $PATH. |
echo '$PATH' affiche la chaine '$PATH'. |
echo 'erreur d'utilisation' affiche > ... commande non terminée. |
echo 'erreur d'\''utilisation' affiche erreur d'utilisation (par appariement des '). |
Le caractère \ anti-slash permet de retirer la signification du caractère spécial du shell le suivant immédiatement. Pour afficher le caractère \, il faut le doubler !
Exemples d'utilisation :
echo * affiche les fichiers présents dans le répertoire courant. |
echo \* affiche le caractère *. |
echo 'erreur d'\''utilisation' affiche erreur d'utilisation (traitement du ' non apparié). |
Les " guillemets ou doubles quotes permettent de retirer la signification de tous les caractères spéciaux du shell sauf $ et ${}, `` et $() et enfin \ ainsi qu'elle-même; à l'instar de ' elles doivent être appariées dans une même ligne de commande, et ne se protègent pas elles-mêmes.
Exemples d'utilisation :
echo "Le contenu de la variable PATH=$PATH" affiche le contenu de la variable $PATH. |
echo "Le contenu de la variable PATH=\"$PATH\"" affiche la chaine '$PATH'. |
echo "ls -l * 1>liste 2&>1" affiche > affiche ls -l * 1>liste 2&>1 |
echo "$(ls -l * 1>liste 2&>1)"' n'affiche rien mais crée le fichier liste. |
Il est des cas particulies où il ne sera pas possible, même en utilisation les possibilités d'échappement, d'obtenir le résultat escompté : il faut alors pouvoir isoler le nom de la variable utilisée (c'est à dire forcer le shell à n'utiliser qu'une partie de la commande pour en extraire une valeur de variable). C'est notamment le cas lors de la concaténation du contenu d'une variable à une chaine de caractères débutant par un des caractères appartenant à l'ensemble du jeu de caractères valides pour le nommage d'une variable.
Cette isolation s'obtient en utilisant la syntaxe ${VARIABLE}. Exemple :
On cherche à créer des fichiers nommés FIC001_LISTE.txt à partir d'une variable NUM contenant des valeurs telles que '001', '002'... la syntaxe
touch FIC$NUM_LISTE.txt aboutira à créer un fichier FIC.txt, dans la mesure où le shell considère une variable NUM_LISTE qui n'est pas définie.
La solution passe donc par isoler le nom de la variable NOM : touch FIC${NUM}_LISTE.txt .
Sous le terme de substitution de variables, se cache simplement la possibilité d'attribuer une valeur par défaut aux variables (qu'elles soient non initialisées).
${variable:-valeur} si la variable n'est pas vide, la substitution retourne sa valeur, sinon elle retourne valeur.
${variable:=valeur} si la variable n'est pas vide, la susbtitution retourne sa valeur, sinon elle retourne valeur et variable est également affectée à valeur.
${variable:+valeur} si la variable est pas vide, la substitution retourne valeur, sinon elle retourne sa valeur, c'est à dire vide.
${variable:?message} si la variable n'est pas vide, la substitution retourne sa valeur, sinon le shell affiche le nom de la variable suivi de la chaine de caractères message. De plus, si cette forme de substitution est utilisée dans un script shell, celui s'arrête immédiatement après l'affichage du message.
Exemples d'utilisation :
VAR=BONJOUR ; echo ${VAR:-'la variable VAR est vide !'} |
affiche "BONJOUR". |
unset VAR ; echo ${VAR:-'la variable VAR est vide !'} |
affiche "la variable VAR est vide !". |
VAR=BONJOUR ; echo ${VAR:='la variable VAR est pas vide !'}; echo $VAR |
affiche 2 fois "BONJOUR". |
unset VAR ; echo ${VAR:='la variable VAR est vide !'}; echo $VAR |
affiche 2 fois "la variable VAR est vide !". |
unset VAR ; echo ${VAR:+'la variable VAR est vide !'} |
n'affiche ... rien. |
VAR=BONJOUR ; echo ${VAR:+'la variable VAR est pas vide !'} |
affiche "la variable VAR est pas vide !". |
unset VAR ; echo ${VAR:?'la variable VAR est vide !'}; echo "suite ..." |
affiche "la variable VAR est vide !". |
VAR=BONJOUR ; echo ${VAR:?'la variable VAR est vide !'}; echo $VAR |
affiche 2 fois "BONJOUR". |
La substitution de commandes permet l'utilisation du résultat de l'exécution d'une commande dans une autre commande, ou de l'affecter à une variable. La syntaxe peut indifféremment prendre 2 formes d'écriture :
$(COMMANDE) ou bien `COMMANDE`
Exemples d'utilisation :
echo "liste des fichiers du répertoire : $(ls)" |
nbre=$(ls -l | wc -l); nbre=`expr $nbre-1`; echo "nbre de fichiers : $nbre" |
L'ensemble des mécanismes vus précédemment peuvent être mis en jeu lors de l'exécution d'une ligne de commande. Ils sont toutefois exécutés dans un ordre précis :
Isolation des mots séparés par caractères séparateurs (par défaut : espace, tabulation, saut de ligne).
Traitement des caractères de protection ('' "" \).
Substitution des variables ($ ${}).
Substitution des commandes (`` $()).
Substitution des caractères de génération des noms de fichiers ('* ? [] ?() +() *() !() @()).
Traitement des tubes et des redirections.
Lancement de la commande.
Le flux d'exécution peut (et doit) être contrôlé en fonction des résultats sucessifs des commandes exécutées ou de l'état de variables d'exécution. Les opérateurs du shell permettent de contrôler simplement le lancement ou non d'une commande en fonction du résultat d'une précédente commande ; alors que les structures de contrôle permettent d'agir plus finement sur le flux d'exécution s'un script.
Les opérateurs lient entre elles différentes commandes, et sont au nombre de deux :
&& signifie ET logique, et exécute la commande suivante si la précédente a retourné VRAI (code retour 0).
|| signifie OU logique, et exécute la commande suivante si la précédente a retourné FAUX (code retour >0).
CMD1 && CMD2 : CMD2 n'est exécutée que si CMD1 a retourné VRAI, l'ensemble ne retourne VRAI que si toutes les commandes ont retourné VRAI.
CMD1 || CMD2 : CMD2 n'est exécutée que si CMD1 a retourné FAUX, l'ensemble retourne VRAI si au moins une des commandes a retourné VRAI.
Exemple d'utilisation :
make bzImage && make modules && make modules_install |
- du noyau lui-même (make bzImage). |
- des modules, si la compilation du noyau a réussi (make modules). |
- et enfin, installer les modules (dans /lib/modules/) si leur compilation a réussi (make modules_install). |
Deux catégories de structures programmatiques permettent de contrôler l'exécution des scripts soit conditionnellement, en fonction de tests ; ou en opérant via des boucles itératives qui permettront donc de répéter une action tant qu'une condition sera vérifiée.
Le shell offre la possibilité d'exécuter des branchements conditionnels dans le flux d'exécution en fonction du résultat d'un ou plusieurs tests qui sont introduits par différentes syntaxes possibles :
soit par la commande interne [ [val1] op val2 ]
soit par la commande interne [[ [val1] op val2 ]]
ou encore par la commande externe test [val1] op val2
en ce qui concerne les expressions arithmétiques il faut utiliser soit la commande interne (( [val1] op val2 )) ou let
ou la commande externe expr [val1] op val2
Tableau 1. Opérateurs de tests portant sur les fichiers :
commande | opérateur | signification | compatibilité | ||
---|---|---|---|---|---|
test ou [ ou [[ | -a nomfic ou -e nomfic | VRAI si le fichier existe | ksh ou bash uniquement | ||
test ou [ ou [[ | -s nomfic | VRAI si le fichier n'est pas vide | sh et suivants | ||
test ou [ ou [[ | -f nomfic | VRAI si le fichier est de type ordinaire | sh et suivants | ||
test ou [ ou [[ | -d nomfic | VRAI si le fichier est un répertoire | sh et suivants | ||
test ou [ ou [[ | -h nomfic | VRAI si le fichier est un lien symbolique | sh et suivants | ||
test ou [ ou [[ | -L nomfic | VRAI si le fichier est un lien symbolique | ksh ou bash uniquement | ||
test ou [ ou [[ | -b nomfic | VRAI si le fichier représente un périphérique bloc | sh et suivants | ||
test ou [ ou [[ | -c nomfic | VRAI si le fichier représente un périphérique caractères | sh et suivants | ||
test ou [ ou [[ | -p nomfic | VRAI si le fichier est un tube nommé | sh et suivants | ||
test ou [ ou [[ | -S nomfic | VRAI si le fichier est un socket | ksh ou bash uniquement | ||
test ou [ ou [[ | -r nomfic | VRAI si le fichier est accessible en lecture | sh et suivants | ||
test ou [ ou [[ | -w nomfic | VRAI si le fichier est accessible en écriture | sh et suivants | ||
test ou [ ou [[ | -x nomfic | VRAI si le fichier est possède le droit d'éxécution | sh et suivants | ||
test ou [ ou [[ | -u nomfic | VRAI si le fichier est possède le setuid-bit | sh et suivants | ||
test ou [ ou [[ | -g nomfic | VRAI si le fichier est possède le setgid-bit | sh et suivants | ||
test ou [ ou [[ | -k nomfic | VRAI si le fichier est possède le sticky-bit | sh et suivants | ||
test ou [ ou [[ | -x nomfic | VRAI si le fichier est possède le sticky-bit | sh et suivants | ||
test ou [ ou [[ | -O nomfic | VRAI si l'utilisateur est propriétaire du fichier | ksh ou bash uniquement | ||
test ou [ ou [[ | -G nomfic | VRAI si l'utilisateur appartient au groupe propriétaire du fichier | ksh ou bash uniquement | ||
test ou [ ou [[ | nomfic1 -nt nomfic2 | VRAI si le fichier nomfic1 est plus récent que le fichier nomfic2 | ksh ou bash uniquement | ||
test ou [ ou [[ | nomfic1 -ot nomfic2 | VRAI si le fichier nomfic1 est plus ancien que le fichier nomfic2 | ksh ou bash uniquement | ||
test ou [ ou [[ | nomfic1 -ef nomfic2 | VRAI si les fichiers nomfic2 et nomfic2 référencent la même inode | ksh ou bash uniquement | ||
test ou [ ou [[ | -t [descripteur] | VRAI si le descripteur (1 par défaut) est associé à un terminal | sh et suivants |
Tableau 2. Opérateurs de tests portant sur les chaines de caractères :
commande | opérateur | signification | compatibilité | ||
---|---|---|---|---|---|
test ou [ ou [[ | -z chaine | VRAI si la chaine est vide | sh et suivants | ||
test ou [ ou [[ | -n chaine ou nomfic | VRAI si la chaine n'est pas vide | sh et suivants | ||
test ou [ ou [[ | chaine1 = chaine2 | VRAI si les deux chaines sont égales | sh et suivants | ||
test ou [ ou [[ | chaine1 != chaine2 | VRAI si les deux chaines sont différentes | sh et suivants | ||
[[ | chaine1 < chaine2 | VRAI si la chaine1 est lexicographiquement avant la chaine2 | ksh ou bash uniquement | ||
[[ | chaine1 > chaine2 | VRAI si la chaine1 est lexicographiquement après la chaine2 | ksh ou bash uniquement | ||
[[ | chaine1 = modèle | VRAI si la chaine correspond au modèle (le modèle est une expression correspondant à la syntaxe des recherche de fichiers) | ksh ou bash uniquement | ||
[[ | chaine1 != modèle | VRAI si la chaine diffère du modèle | ksh ou bash uniquement | ||
expr ou (( | chaine1 \& chaine2 | VRAI si les 2 chaines ne sont pas nulles | sh et suivants | ||
expr ou (( | chaine1 \| chaine2 | VRAI si l'une des 2 chaines n'est pas nulle | sh et suivants | ||
expr ou (( | chaine : expression régulière | compare la chaine à l'expression régulière. | sh et suivants |
Tableau 3. Opérateurs de tests portant sur des nombres :
commande | opérateur | signification | compatibilité | ||
---|---|---|---|---|---|
test ou [ ou [[ | nb1 -eq nb2 | VRAI si les nombres sont égaux | sh et suivants | ||
test ou [ ou [[ | nb1 -ne nb2 | VRAI si les nombres sont différents | sh et suivants | ||
test ou [ ou [[ | nb1 -lt nb2 | VRAI si nb1 est strictement inférieur à nb2 | sh et suivants | ||
test ou [ ou [[ | nb1 -le nb2 | VRAI si nb1 est strictement inférieur ou égal à nb2 | sh et suivants | ||
test ou [ ou [[ | nb1 -gt nb2 | VRAI si nb1 est strictement supérieur à nb2 | sh et suivants | ||
test ou [ ou [[ | nb1 -ge nb2 | VRAI si nb1 est strictement supérieur ou égal à nb2 | sh et suivants | ||
expr ou (( | nb1 + nb2 | addition | sh et suivants | ||
expr ou (( | nb1 - nb2 | soustraction | sh et suivants | ||
expr ou (( | nb1 * nb2 | multiplication | sh et suivants | ||
expr ou (( | nb1 / nb2 | division | sh et suivants | ||
expr ou (( | nb1 % nb2 | modulo | sh et suivants | ||
(( uniquement | ~nb1 | complément à 1 | ksh et bash uniquement | ||
(( uniquement | nb1 > nb2 | décalage à droite de nb2 bits sur nb1 | ksh et bash uniquement | ||
(( uniquement | nb1 < nb2 | décalage à gauche de nb2 bits sur nb1 | ksh et bash uniquement | ||
(( uniquement | nb1 & nb2 | ET bit à bit | ksh et bash uniquement | ||
(( uniquement | nb1 | nb2 | OU bit à bit | ksh et bash uniquement | ||
(( uniquement | nb1 ^ nb2 | OU EXCLUSIF bit à bit | ksh et bash uniquement | ||
expr ou (( | nb1 \> nb2 | VRAI si nb1 est strictement supérieur à nb2 | sh et suivants | ||
expr ou (( | nb1 \< nb2 | VRAI si nb1 est strictement inférieur à nb2 | sh et suivants | ||
expr ou (( | nb1 \>= nb2 | VRAI si nb1 est supérieur ou égal à nb2 | sh et suivants | ||
expr ou (( | nb1 \<= nb2 | VRAI si nb1 est inférieur ou égal à nb2 | sh et suivants | ||
expr ou (( | nb1 = nb2 | VRAI si nb1 est égal à nb2 | sh et suivants | ||
(( uniquement | nb1 == nb2 | VRAI si nb1 est égal à nb2 | ksh ou bash | ||
(( uniquement | nb1 [opération arithmétique]= nb2 | assignement (éventuellement précédé de l'opération) | ksh ou bash | ||
expr ou (( | nb1 != nb2 | VRAI si nb1 est différent de nb2 | sh et suivants | ||
expr ou (( | -nb1 | opposé de nb1 | sh et suivants | ||
expr ou (( | -nb1 | opposé de nb1 | sh et suivants |
Afin de compléter cette armada de tests, le shell dispose également d'opérateurs permettant d'influer sur un test ou lier logiquement plusieurs tests :
L'opérateur ! : signifie la négation du test le suivant.
L'opérateur -a : effectue un ET logique entre deux tests.
L'opérateur -o : effectue un OU logique entre deux tests.
La syntaxe de la commande [[ ... ]] s'est vue enrichie par rapport à la commande test (ou [ ... ]) basique, et notamment en ce qui concerne les chaines de caractères pour lesquelles l'usage des guillemets n'est plus nécessaire, certains opérateurs ont été rajoutés (cf. tableau) et les opérateurs de négation et de regroupement ont été alignés sur la syntaxe du C :
L'opérateur ! reste tel quel.
L'opérateur -a (ET logique) devient && (idem pour la commande(( ... ))).
L'opérateur -o (OU logique) devient || (idem pour la commande (( ... ))).
Et les parenthèses de regroupement ne doivent plus être échappées.
Exemples d'utilisation :
test -a lefichier; echo $? : teste l'existence du fichier lefichier. |
[ -a lefichier ]; echo $? : teste l'existence du fichier lefichier. |
[ -b /dev/sda ]; echo $? : teste si /dev/sda est un périphérique bloc. |
[ "ch1" ="$ch2" ]; echo $? : teste l'égalité des deux variables chaine ch1 et ch2. |
[ ch1 =$ch2 ]; echo $? : teste l'égalité des deux variables chaine ch1 et ch2. |
[[ ch1 =$ch2 ]]; echo $? : teste l'égalité des deux variables chaine ch1 et ch2. |
[ -w $fic1 -a \( -e $rep1 -o -e $rep2 \) ]; echo $? : devinez ... |
[[ -w $fic1 && ( -e $rep1 || -e $rep2 ) ]]; echo $? : devinez ... |
X=10; echo `expr $X + 10`; echo $?; echo $X : calcule X + 10, X reste identique. |
X=10; echo $((($X + 10))); echo $?; echo $X : calcule X + 10, X reste identique. |
Les tests peuvent donc permettre d'éxecuter conditionnellement un partie d'un programme en fonction de son résultat. La structure pour procéder à ces branchements dans le code est la structure if : si la condition est vraie, alors le programme exécute un bloc de code. Mais elle peut se compliquer quelque peu en rajoutant les notions de sinon (else) et de sinon, si (elif). La syntaxe est la suivante :
if test |
then |
... bloc de code ... |
[elif test] |
then |
... bloc de code ... |
[else] |
... bloc de code ... |
fi |
if test ; then |
... bloc de code ... |
[elif test] ; then |
... bloc de code ... |
[else] |
... bloc de code ... |
fi |
Exemples d'utilisation :
if [ -a ~/.bashrc ] ; then |
echo "J'ai bien un fichier de configuration personnalisé pour le shell bash !" |
echo "En voici son contenu :" |
cat ~/.bashrc |
fi |
if [ -z $a ] ; then |
echo "La variable a est indéfinie !" |
exit 1 |
fi |
if (( $a == 10)) ; then |
echo "La variable a vaut $a !" |
elif [ $a -eq 20 ] ; then |
echo "La variable a vaut $a !" |
elif test $a -lt 20 ; then |
echo "La valeur de la variable a est inférieure à 20 ..." |
echo "La valeur de la variable a est différente de 10 ..." |
else |
echo "La valeur de la variable a est supérieure à 20 ..." |
fi |
Dans certains cas, il sera possible de remplacer l'utilisation de nombreuses conditions if - elif qui deviennent rapidement illisibles par la structure case. Sa syntaxe est la suivante :
case $variable in |
modele1) |
... bloc de code ... |
;; |
modele2) |
... bloc de code ... |
;; |
*) |
... bloc de code ... |
;; |
esac |
Tableau 4. Caractères génériques servant à forger un modèle :
caractère spécial | signifie | compatibilité |
---|---|---|
* | 0 à n caractères quelconques | sh et suivants |
? | 1 caractère quelconque | sh et suivants |
[aAbBc] | 1 caractère parmis ceux figurant entre les crochets | sh et suivants |
[!aAbBc] | 1 caractère parmis ceux ne figurant pas entre les crochets | sh et suivants |
?(expression) | 0 ou 1 fois l'expression entre parenthèses | ksh ou bash uniquement ; pour bash, il faut activer l'option shopt -s extglob |
*(expression) | 0 à n fois fois l'expression entre parenthèses | ksh ou bash uniquement ; pour bash, il faut activer l'option shopt -s extglob |
+(expression) | 0 à n fois fois l'expression entre parenthèses | ksh ou bash uniquement ; pour bash, il faut activer l'option shopt -s extglob |
@(expression) | 1 fois fois l'expression entre parenthèses | ksh ou bash uniquement ; pour bash, il faut activer l'option shopt -s extglob |
!(expression) | 0 fois fois l'expression entre parenthèses | ksh ou bash uniquement ; pour bash, il faut activer l'option shopt -s extglob |
Exemple d'utilisation :
case "$1" in |
start) |
echo "Démarrage du daemon ..." |
;; |
stop) |
echo "Arrêt du daemon ..." |
;; |
*) |
echo "Usage : $SCRIPTNAME {start|stop}" |
exit 3 |
;; |
esac |
Les boucles sont des structures de contrôle permettant d'itérer un bloc de code autant de fois que nécessaire. L'itération peut être déterminée soit par le nombre de valeurs d'une liste dans le cas de la boucle for, soit par le résultat d'un commande ou d'un test dans le cas des boucles while et until :
Le bloc code compris dans un boucle for sera exécuté pour chaque élement de la liste ; la liste peut être fournie explicitement, par substitution de variable, par susbtition de commande ou par évaluation des caractères spéciaux du shell. Si aucune liste n'est fournie, la boucle porte sur les arguments passés dans la ligne de commande (évaluation de la variable $*. Lors de chaque itération, la variable de boucle prend la valeur suivante dans la liste.
Le bloc code compris dans un boucle while sera exécuté tant que la valeur retournée par la commande de contrôle sera VRAI ; dès qu'elle retournera FAUX, le flux d'exécution passe à la commande suivant immédiatement le mot-clé done.
Le bloc code compris dans un boucle until sera exécuté tant que la valeur retournée par la commande de contrôle n'est pas VRAI.
for var [in liste] |
do |
... bloc de code ... |
done |
ou |
for var [in liste] ; do |
... bloc de code ... |
done |
while expression |
do |
... bloc de code ... |
done |
ou |
while expression ; do |
... bloc de code ... |
done |
until expression |
do |
... bloc de code ... |
done |
ou |
until expression ; do |
... bloc de code ... |
done |
Exemples d'utilisation :
for i in *; do ls -i; done; |
for i in $(cat /etc/passwd | cut -d: -f1); do echo $i; done; |
for i in `seq 1 10` ; do echo $i; done |
for i in 1 2 3 4 5 6 7 8 9 10 ; do echo $i; done |
while true ; do echo "Saisissez un chiffre"; read CHIFFRE; echo "$CHIFFRE erroné. essayez encore..."; done; |
REP=0; while (( $REP < 10 )); do echo $REP; REP=`expr $REP + 1`; done; |
until false ; do echo "Saisissez un chiffre"; read CHIFFRE; echo "$CHIFFRE erroné. essayez encore..."; done; |
REP=0; until [ $REP -eq 10 ] ; do echo $REP; ((REP+=1)); done; |
Lors de l'exécution d'une boucle, les commandes internes continue et break permettent de modifier le flux de commandes en, respectivement, passant à l'itération suivante et en interrompant le cours de la boucle.
Exemples d'utilisation :
for i in `seq 1 10` do if (( $i == 5 )) then continue # n'affichera pas 5 fi if (( $i > 8 )) # interromp la boucle pour i=9 then break fi echo $i done clear while true ; do # boucle infinie echo "Saisissez un nombre (0 non traité - 999 pour terminer)"; read NOMBRE; if [ ! $(echo $NOMBRE | grep "^[ [:digit:] ]*$") ] ; then echo "On vous a demandé un nombre !" continue fi if [ $NOMBRE -eq 0 ] ; then continue elif [ $NOMBRE -eq 999 ] ; then break else echo "Vous avez saisi le nombre $NOMBRE (999 pour terminer)"; sleep 5 clear fi done;
Dernière forme de structuration du code, le shell prévoit la possibilité de création de fonctions qui permettent de factoriser des portions de code destinées à être exécutées plusieurs fois dans un script, ou bien encore de créer des librairies de code réutilisables dans différents scripts. Une fonction doit être définie avant son utilisation (son appel) ; la syntaxe pour la définition d'une fonction est la suivante :
nomdelafonction() { |
... bloc de code ... |
} |
Une seconde forme est également possible (uniquement avec ksh ou bash) |
function nomdelafonction { |
... bloc de code ... |
} |
nomdelafonction [parametre1 parametre2 ...] |
Une function, comme obéit aux mêmes principes que toute commande Unix : elle retourne un code de retour (un entier compris entre 0 et 255) qui vaut par défaut 0, mais cette valeur peut être modifiée en utilisant la commande interne return [valeur]. Ce code de retour est transmis à la variable spéciale $? dès le retour de la fonction, et peut donc être testé comme de coutume.
Les variables utilisateur sont globales pour l'ensemble d'un script, c'est à dire qu'une variable déclarée dans le script sera accessible et modifiable dans le corps d'une fonction, et qu'à l'identique une variable déclarée ou affectée dans le corps d'une fonction sera accessible au reste du script. Or, dans nombre de cas, les variables de travail d'une fonction n'ont à être accessibles ni publiées à l'extérieur de celle-ci ; la commande interne typeset permet de déclarer des variables locales dans une fonction : elle sont alors ni accessibles ni publiées en dehors du corps de la fonction.
Quant aux paramètres passés à une fonction, ils sont accessibles à celle-ci via le jeu de variables spéciales locales $1, $2, ..., $*, $@, $#.
La variable spéciale $0 reste quant à elle accessible, en lecture-seule, et contient toujours le nom du script en cours d'exécution. |
#!/bin/bash function saisie_nombre { echo -n "$0> Saisissez un nombre (999 pour terminer) : " read NOMBRE; if [ ! $(echo $NOMBRE | grep "^[ [:digit:] ]*$") ] ; then NOMBRE="" return 1 fi } clear while true ; do # boucle infinie saisie_nombre RES=$? if [ $RES -ne 0 ] ; then echo "On vous a demandé un nombre !" continue fi if [ $NOMBRE -eq 0 ] ; then continue elif [ $NOMBRE -eq 999 ] ; then break else echo "Vous avez saisi le nombre $NOMBRE (999 pour terminer)"; sleep 5 clear fi done;