Une boucle est un bloc de code qui répète une liste de commandes aussi longtemps que la condition de contrôle de la boucle est vraie.
C'est la construction de boucle de base. Elle diffère de façon significative de sa contre partie en C.
for arg in [liste]
do
commande(s)...
done
![]() | A chaque passage dans la boucle, arg prend successivement la valeur de toutes les variables de la liste. |
for arg in "$var1" "$var2" "$var3" ... "$varN" # Lors du tour 1 de la boucle, $arg = $var1 # Lors du tour 2 de la boucle, $arg = $var2 # Lors du tour 3 de la boucle, $arg = $var3 # ... # Lors du tour N de la boucle, $arg = $varN # Les arguments dans [liste] sont mis entre guillemets pour empêcher une #+ possible séparation des mots. |
L'argument liste peut contenir des caractères joker.
Si do est sur la même ligne que for, il est impératif d'avoir un point virgule après la liste.
for arg in [liste] ; do
Exemple 10-1. Des boucles for simples
#!/bin/bash # Liste les planètes. for planete in Mercure Vénus Terre Mars Jupiter Saturne Uranus Neptune Pluton do echo $planete done echo # La 'liste' entière entourée par des guillemets crée une variable simple. for planete in "Mercure Vénus Terre Mars Jupiter Saturne Uranus Neptune Pluton" do echo $planete done exit 0 |
![]() | Chaque élément de la [liste] peut contenir de multiples paramètres. C'est utile pour travailler sur des paramètres en groupe. Dans de tels cas, utilisez la commande set (voir Exemple 11-13) pour forcer l'analyse de chaque élément de la [liste] et l'affectation de chaque composant aux paramètres positionels. |
Exemple 10-2. Boucle for avec deux paramètres dans chaque élément de la [liste]
#!/bin/bash # Planètes revisitées. # Associe le nom de chaque planète à sa distance du soleil. for planete in "Mercure 36" "Vénus 67" "Terre 93" "Mars 142" "Jupiter 483" do set -- $planete # Analyse la variable "planete" et initialise les paramètres #+ de position. # Le "--" empêche de mauvaises surprises si $planete est nul ou commence avec #+ un tiret. # Il peut être utile de sauvegarder les paramètres de position originaux, car #+ ils seront écrasés. # Une façon de le faire est d'utiliser un tableau, # parametres_originaux=("$@") echo "$1 $2.000.000 miles du soleil" #-------deux tabulations---concatènent les zéros dans le paramètre $2 done # (Merci, S.C., pour les clarifications supplémentaires.) exit 0 |
Une variable peut fournir la [liste] dans une boucle for.
Exemple 10-3. Fileinfo: opérer sur une liste de fichiers contenue dans une variable
#!/bin/bash # fileinfo.sh FICHIERS="/usr/sbin/privatepw /usr/sbin/pwck /usr/sbin/go500gw /usr/bin/fakefile /sbin/mkreiserfs /sbin/ypbind" # Liste de fichiers qui vous intéressent. # Envoyez-les dans un fichier quelconque, /usr/bin/fauxfichier. echo for fichier in $FILES do if [ ! -e "$fichier" ] # Vérifie si le fichier existe. then echo "$fichier n'existe pas."; echo continue # Au suivant. fi ls -l $fichier | awk '{ print $9 " taille: " $5 }' # Affiche 2 champs. whatis `basename $fichier` # Informations sur le fichier. echo done exit 0 |
La [liste] dans une boucle for peut contenir un remplacement des noms de fichier, c'est-à-dire utiliser des jokers pour l'expansion de noms de fichiers.
Exemple 10-4. Agir sur des fichiers à l'aide d'une boucle for
#!/bin/bash # list-glob.sh: Générer une [liste] dans une boucle for en utilisant "globbing". echo for fichier in * do ls -l "$fichier" # Liste tous les fichiers de $PWD (répertoire courant). # Rappelez-vous que le caractère joker "*" correspond à chaque nom de fichier, # néanmoins, lors du "globbing", il ne récupère pas les fichier commençant # par un point. # Si le modèle ne correspond à aucun fichier, il s'étend à lui-même. Pour # empêcher ceci, utilisez l'option nullglob # (shopt -s nullglob). # Merci, S.C. done echo; echo for fichier in [jx]* do rm -f $fichier # Supprime seulement les fichiers commençant par un "j" ou # un "x" dans $PWD. echo "Suppression du fichier \"$fichier\"". done echo exit 0 |
Omettre la partie in [liste] d'une boucle for fait en sorte que la boucle opère sur $@, la liste des arguments donnés sur la ligne de commande du script. Une illustration particulièrement intelligente de ceci est Exemple A-17.
Exemple 10-5. in [liste] manquant dans une boucle for
#!/bin/bash # Appeler à la fois avec et sans arguments, et voir ce que cela donne. for a do echo -n "$a " done # La 'liste' est manquante, donc la boucle opère sur '$@' #+ (la liste d'arguments sur la ligne de commande, incluant les espaces blancs). echo exit 0 |
Il est possible d'utiliser la substitution de commandes pour générer la [liste] d'une boucle for. Voir aussi Exemple 12-39, Exemple 10-10 et Exemple 12-33.
Exemple 10-6. Générer la [liste] dans une boucle for avec la substitution de commandes
#!/bin/bash # Une boucle for avec une [liste] générée par une substitution de commande. NOMBRES="9 7 3 8 37.53" for nombre in `echo $NOMBRE` # for nombre in 9 7 3 8 37.53 do echo -n "$nombre " done echo exit 0 |
Voici un exemple un peu plus complexe de l'utilisation de la substitution de commandes pour créer la [liste].
Exemple 10-7. Un remplaçant de grep pour les fichiers binaires
#!/bin/bash # bin-grep.sh: Trouve les chaînes de caractères correspondantes dans un fichier #+ binaire. # Un remplacement de "grep" pour les fichiers binaires. # Similaire par son effet à "grep -a" E_BADARGS=65 E_NOFILE=66 if [ $# -ne 2 ] then echo "Usage: `basename $0` chaine nomfichier" exit $E_BADARGS fi if [ ! -f "$2" ] then echo "Le fichier \"$2\" n'existe pas." exit $E_NOFILE fi for word in $( strings "$2" | grep "$1" ) # La commande "strings" liste les chaînes de caractères dans les fichiers #+ binaires. # Sortie envoyée via un tube dans "grep", qui cherche la chaîne désirée. do echo $word done # Comme S.C. l'a indiqué, la boucle for ci-dessus pourrait être remplacée avec #+ la chaîne # strings "$2" | grep "$1" | tr -s "$IFS" '[\n*]' # Essayez quelque chose comme "./bin-grep.sh mem /bin/ls" pour comprendre ce #+ script. exit 0 |
Un peu la même chose.
Exemple 10-8. Afficher tous les utilisateurs du système
#!/bin/bash # userlist.sh FICHIER_MOTS_DE_PASSE=/etc/passwd n=1 # Nombre d'utilisateurs for nom in $(awk 'BEGIN{FS=":"}{print $1}' < "$FICHIER_MOTS_DE_PASSE" ) # Champ séparateur = : ^^^^^^ # Affiche le premier champ ^^^^^^^^ # Obtient l'entrée à partir du fichier ^^^^^^^^^^^^^^^^^^^^^^ do echo "UTILISATEUR #$n = $nom" let "n += 1" done # UTILISATEUR #1 = root # UTILISATEUR #2 = bin # UTILISATEUR #3 = daemon # ... # UTILISATEUR #30 = bozo exit 0 |
Un dernier exemple d'une [liste] résultant d'une substitution de commande.
Exemple 10-9. Rechercher les auteurs de tous les binaires d'un répertoire
#!/bin/bash # findstring.sh: # Cherche une chaîne de caractères particulière dans des binaires d'un #+ répertoire particulier. repertoire=/usr/bin/ chainef="Free Software Foundation" # Voir quels fichiers viennent de la FSF. for fichier in $( find $repertoire -type f -name '*' | sort ) do strings -f $fichier | grep "$chainef" | sed -e "s%$repertoire%%" # Dans l'expression "sed", il est nécessaire de substituer le délimiteur #+ standard "/" parce que "/" se trouve être un caractère filtré. Ne pas le #+ faire provoque un message d'erreur (essayez). done exit 0 # Exercice (facile): # --------------- # Convertir ce script pour prendre en paramètres de ligne de commande les #+ variables $repertoire et $chainef. |
La sortie d'une boucle for peut être envoyée via un tube à une ou plusieurs commandes.
Exemple 10-10. Afficher les liens symboliques dans un répertoire
#!/bin/bash # symlinks.sh: Liste les liens symboliques d'un répertoire. repertoire=${1-`pwd`} # Par défaut, le répertoire courant, si le répertoire n'est pas sépcifié. # Equivalent au bloc de code ci-dessous. # ----------------------------------------------------------------- # ARGS=1 # Attend un argument en ligne de commande. # # if [ $# -ne "$ARGS" ] # Si sans argument... # then # repertoire=`pwd` # répertoire courant # else # repertoire=$1 # fi # ----------------------------------------------------------------- echo "Liens symboliques du répertoire \"$repertoire\"" for fichier in "$( find $repertoire -type l )" # -type l = liens symboliques do echo "$fichier" done | sort # Sinon la liste de fichiers n'est pas trié. # Comme Dominik 'Aeneas' Schnitzer l'indique, ne pas mettre entre guillemets #+ $( find $repertoire -type l ) #+ fera échouer le script sur les noms de fichier comprenant des espaces. # Même ceci ne prendra que le premier champ de chaque argument. exit 0 |
Le stdout d'une boucle peut être redirigée vers un fichier, comme cette légère modification du précédent exemple le montre.
Exemple 10-11. Liens symboliques dans un répertoire, sauvés dans un fichier
#!/bin/bash # symlinks.sh: Liste les liens symboliques dans un répertoire. FICHIER_DE_SORTIE=liste.liens_symboliques # fichier de sauvegarde repertoire=${1-`pwd`} # Par défaut, le répertoire courant si aucun autre n'a été spécifié. echo "liens symboliques dans le répertoire \"$repertoire\"" > "$FICHIER_DE_SORTIE" echo "----------------------------------------------------" >> "$FICHIER_DE_SORTIE" for fichier in "$( find $repertoire -type l )" # -type l = liens symboliques do echo "$fichier" done | sort >> "$FICHIER_DE_SORTIE" # stdout de la boucle # ^^^^^^^^^^^^^^^^^^ redirigé vers le fichier de sauvegarde. exit 0 |
Il existe une autre syntaxe pour une boucle for ressemblant fortement à celle du C. Elle nécessite des parenthèses doubles.
Exemple 10-12. Une boucle for à la C
#!/bin/bash # Deux façons de compter jusqu'à 10. echo # Syntaxe standard. for a in 1 2 3 4 5 6 7 8 9 10 do echo -n "$a " done echo; echo # +==========================================+ # Maintenant, faisons de même en utilisant une syntaxe C. LIMITE=10 for ((a=1; a <= LIMITE ; a++)) # Double parenthèses, et "LIMITE" sans "$". do echo -n "$a " done # Une construction empruntée à 'ksh93'. echo; echo # +=========================================================================+ # Utilisons l'opérateur "virgule" C pour incrémenter deux variables en même #+ temps. for ((a=1, b=1; a <= LIMITE ; a++, b++)) # La virgule chaîne les opérations. do echo -n "$a-$b " done echo; echo exit 0 |
Voir aussi Exemple 26-8, Exemple 26-9 et Exemple A-7.
---
Maintenant, une boucle for utilisée dans un contexte de la << vie quotidienne >>.
Exemple 10-13. Utiliser efax en mode batch
#!/bin/bash ARGUMENTS_ATTENDUS=2 E_MAUVAISARGS=65 if [ $# -ne $ARGUMENTS_ATTENDUS ] # Vérifie le bon nombre d'arguments en ligne de commande. then echo "Usage: `basename $0` téléphone# fichier-texte" exit $E_MAUVAISARGS fi if [ ! -f "$2" ] then echo "Le fichier $2 n'est pas un fichier texte" exit $E_MAUVAISARGS fi fax make $2 # Crée des fichiers formatés pour le fax à partir de #+ fichiers texte. for fichier in $(ls $2.0*) # Concatène les fichiers convertis. # Utilise le caractère joker dans la liste des variables. do fic="$fic $fichier" done efax -d /dev/ttyS3 -o1 -t "T$1" $fic # Fait le boulot. # Comme S.C. l'a indiqué, la boucle for peut être supprimée avec # efax -d /dev/ttyS3 -o1 -t "T$1" $2.0* # mais ce n'est pas aussi instructif [grin]. exit 0 |
Cette construction teste une condition au début de la boucle et continue à boucler tant que la condition est vraie (renvoie un 0 code de sortie). Par opposition à une boucle for, une boucle while trouve son utilité dans des situations où le nombre de répétitions n'est pas connu dès le départ.
while [condition]
do
commande...
done
Comme c'est le cas avec les boucles for/in, placez le do sur la même ligne que le test de la condition nécessite un point virgule.
while [condition] ; do
Notez que certaines boucles while spécialisées, comme par exemple une construction getopts, dévie quelque peu du modèle standard donné ici.
Exemple 10-14. Simple boucle while
#!/bin/bash var0=0 LIMITE=10 while [ "$var0" -lt "$LIMITE" ] do echo -n "$var0 " # -n supprime le retour chariot. var0=`expr $var0 + 1` # var0=$(($var0+1)) fonctionne aussi. done echo exit 0 |
Exemple 10-15. Une autre boucle while
#!/bin/bash echo while [ "$var1" != "fin" ] # while test "$var1" != "end" do # fonctionne aussi. echo "Variable d'entrée #1 (quitte avec fin) " read var1 # pas de 'read $var1' (pourquoi?). echo "variable #1 = $var1" # A besoin des guillemets à cause du "#". # Si l'entrée est 'end', l'affiche ici. # Ne teste pas la condition de fin avant de revenir en haut de la boucle. echo done exit 0 |
Une boucle while peut avoir de multiples conditions. Seule la condition finale détermine quand la boucle se termine. Ceci nécessite une syntaxe de boucle légèrement différente, malgré tout.
Exemple 10-16. Boucle while avec de multiples conditions
#!/bin/bash var1=unset precedent=$var1 while echo "Variable précédente = $precedent" echo precedent=$var1 [ "$var1" != fin ] # Garde trace de ce que $var1 valait précédemment. # Quatre conditions sur "while", mais seule la dernière contrôle la #+ boucle. # Le *dernier* code de sortie est celui qui compte. do echo "Variable d'entrée #1 (quitte avec fin) " read var1 echo "variable #1 = $var1" done # Essayez de comprendre comment cela fonctionne. # il y a un peu d'astuces. exit 0 |
Comme pour une boucle for, une boucle while peut employer une syntaxe identique à C en utilisant la construction avec des parenthèses doubles (voir aussi Exemple 9-28).
Exemple 10-17. Syntaxe à la C pour une boucle while
#!/bin/bash # wh-loopc.sh: Compter jusqu'à 10 dans une boucle "while". LIMITE=10 a=1 while [ "$a" -le $LIMITE ] do echo -n "$a " let "a+=1" done # Pas de surprises, jusqu'ici. echo; echo # +=================================================================+ # Maintenant, de nouveau mais avec une syntaxe C. ((a = 1)) # a=1 # Les double parenthèses permettent les espaces pour initialiser une variable, #+ comme en C. while (( a <= LIMITE )) # Double parenthèses, et pas de "$" devant la variable. do echo -n "$a " ((a += 1)) # let "a+=1" # Oui, en effet. # Les double parenthèses permettent d'incrémenter une varibale avec une #+ syntaxe style C. done echo # Maintenant, les programmeurs C se sentent chez eux avec Bash. exit 0 |
![]() | Une boucle while peut avoir son stdin redirigé vers un fichier par un < à la fin. |
Cette construction teste une condition au début de la boucle et continue à boucler tant que la condition est fausse (l'opposé de la boucle while).
until [condition-est-vraie]
do
commande...
done
Notez qu'une boucle until teste la condition de fin au début de la boucle, contrairement aux constructions similaires dans certains langages de programmation.
Comme c'est la cas avec les boucles for/in, placez do sur la même ligne que le test de la condition nécessite un point virgule.
until [condition-est-vraie] ; do
Précédent | Sommaire | Suivant |
Boucles et branchements | Niveau supérieur | Boucles imbriquées |