Les boucles Bash permettent de répéter une action sur chaque élément d'un ensemble. Avec for, vous itérez sur une liste connue, serveurs, fichiers, éléments d'un tableau. Avec while, vous traitez un flux dont la taille n'est pas connue à l'avance, chaque ligne d'un fichier de configuration, chaque entrée d'un find. Ce sont les deux outils les plus utilisés dans les scripts d'administration Linux.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Écrire une boucle
forsur une liste, un tableau, des globs de fichiers et des plages numériques - Utiliser
while IFS= read -rpour lire un fichier ligne par ligne sans perdre les espaces - Lire la sortie d'une commande dans une boucle
while - Contrôler l'exécution avec
breaketcontinue - Choisir entre
for,whileetuntilen fonction du contexte
Dans quel contexte ?
Section intitulée « Dans quel contexte ? »Les boucles interviennent dès qu'une action doit être répétée sur plusieurs éléments :
- vérifier le statut de dix services et afficher ceux qui sont arrêtés
- renommer en lot tous les fichiers
.logd'un répertoire en.log.bak - lire
/etc/passwdligne par ligne pour extraire les utilisateurs avec un UID supérieur à 1000 - attendre qu'un port soit ouvert avant de continuer un déploiement
- appliquer un patch à une liste de serveurs passée en arguments
En préparation RHCSA, l'itération sur une liste (for) et la lecture de fichier (while read) sont testées dans les exercices de scripting.
for, itérer sur une liste
Section intitulée « for, itérer sur une liste »Liste littérale
Section intitulée « Liste littérale »for serveur in web01 web02 db01; do echo "Traitement de $serveur"done# Traitement de web01# Traitement de web02# Traitement de db01services=("nginx" "postgresql" "redis" "sshd")for svc in "${services[@]}"; do echo "Service : $svc"done# Service : nginx# Service : postgresql# Service : redis# Service : sshdGlob de fichiers
Section intitulée « Glob de fichiers »for f in /etc/*.conf itère sur les fichiers correspondant au pattern :
count=0for f in /etc/*.conf; do [[ -f "$f" ]] || continue # ignorer les entrées qui ne sont pas des fichiers echo "$f ($(wc -l < "$f") lignes)" count=$((count + 1)) (( count >= 5 )) && breakdone# /etc/adduser.conf (109 lignes)# /etc/ca-certificates.conf (157 lignes)# /etc/debconf.conf (83 lignes)# /etc/deluser.conf (41 lignes)# /etc/dhcpcd.conf (48 lignes)Plage numérique avec {debut..fin} et {debut..fin..pas}
Section intitulée « Plage numérique avec {debut..fin} et {debut..fin..pas} »for i in {1..5}; do printf "%d " "$i"done# 1 2 3 4 5
for i in {0..20..5}; do # pas de 5 printf "%d " "$i"done# 0 5 10 15 20Style C, for (( init; condition; incrément ))
Section intitulée « Style C, for (( init; condition; incrément )) »Utile pour les index ou compteurs avec logique arithmétique :
for (( i=1; i<=5; i++ )); do printf "%d " "$i"done# 1 2 3 4 5
# Compter à reboursfor (( i=10; i>=1; i-- )); do printf "%d " "$i"done# 10 9 8 7 6 5 4 3 2 1Sortie de commande avec substitution
Section intitulée « Sortie de commande avec substitution »for ip in $(cat /etc/hosts | awk '{print $1}' | grep -v '^#'); do echo "Hôte : $ip"donewhile, répéter tant qu'une condition est vraie
Section intitulée « while, répéter tant qu'une condition est vraie »Compteur
Section intitulée « Compteur »compteur=1while (( compteur <= 3 )); do echo "Itération $compteur" compteur=$((compteur + 1))done# Itération 1# Itération 2# Itération 3Lire un fichier ligne par ligne
Section intitulée « Lire un fichier ligne par ligne »C'est le patron canonique pour traiter un fichier texte sans perdre les espaces en début de ligne ni les backslashes :
while IFS= read -r ligne; do echo "→ $ligne"done < fichier.txt# → nginx# → postgresql# → redisDécomposition :
IFS=, désactive la séparation par espaces/tabulations pendantreadread -r, préserve les backslashes tels quels< fichier.txt, redirige le fichier vers l'entrée standard de la boucle
Lire la sortie d'une commande
Section intitulée « Lire la sortie d'une commande »count=0while IFS=: read -r user _ uid _; do (( uid < 100 )) || continue echo "$user (uid=$uid)" count=$((count + 1)) (( count >= 3 )) && breakdone < /etc/passwd# root (uid=0)# daemon (uid=1)# bin (uid=2)IFS=: indique que les champs sont séparés par : (format de /etc/passwd). _ est une variable muette pour ignorer les champs non utiles.
Avec substitution de processus (pour une commande au lieu d'un fichier) :
while IFS= read -r ligne; do echo "→ $ligne"done < <(find /var/log -name "*.log" -newer /etc/passwd)< <(commande) équivaut à rediriger la sortie de commande dans la boucle, sans créer de sous-shell pour la boucle elle-même, ce qui permet de modifier des variables à l'intérieur.
until, répéter jusqu'à ce qu'une condition soit vraie
Section intitulée « until, répéter jusqu'à ce qu'une condition soit vraie »until est l'inverse de while : la boucle continue tant que la condition est fausse :
n=3until (( n <= 0 )); do echo "Compte à rebours : $n" n=$((n - 1))done# Compte à rebours : 3# Compte à rebours : 2# Compte à rebours : 1Cas d'usage typique : attendre qu'un service soit disponible.
until nc -z localhost 5432 2>/dev/null; do echo "PostgreSQL pas encore prêt, nouvelle tentative dans 2s..." sleep 2doneecho "PostgreSQL disponible."break et continue
Section intitulée « break et continue »break: sort de la boucle immédiatementcontinue: passe à l'itération suivante sans exécuter le reste du corps
for i in {1..10}; do (( i % 2 != 0 )) && continue # ignorer les impairs (( i > 8 )) && break # s'arrêter à 8 printf "%d " "$i"doneecho ""# 2 4 6 8break N et continue N permettent de sortir ou passer à l'itération d'une boucle externe imbriquée (N = niveau) :
for hote in web01 web02; do for port in 80 443 8080; do nc -z "$hote" "$port" 2>/dev/null && continue 2 # port ouvert : passer à l'hôte suivant echo "$hote:$port FERMÉ" donedoneChoisir la bonne boucle
Section intitulée « Choisir la bonne boucle »| Situation | Boucle recommandée |
|---|---|
| Liste connue (serveurs, fichiers, arguments) | for item in liste |
| Plage numérique ou compteur simple | for i in {1..N} ou for ((i=0; ...)) |
| Taille inconnue, lire fichier ou commande | while IFS= read -r |
| Attendre une condition externe | until condition |
| Condition dépend d'un compteur modifiable | while (( compteur < MAX )) |
Dépannage
Section intitulée « Dépannage »| Symptôme | Cause probable | Solution |
|---|---|---|
Variables modifiées dans le while perdues après | Boucle dans un sous-shell via pipe | Utiliser while ... done < <(cmd) au lieu de cmd | while |
Glob *.conf passe la chaîne littérale | Aucun fichier ne correspond | Vérifer avec [[ -f "$f" ]] || continue |
{1..N} avec variable : {1..$n} ne s'expande pas | Les accolades s'expandent avant les variables | Utiliser seq "$n" ou for (( i=1; i<=n; i++ )) |
for ligne in $(cat fichier) découpe sur les espaces | Word splitting actif | Utiliser while IFS= read -r ligne; do ... done < fichier |
break dans un sous-shell ne sort pas de la boucle principale | Boucle + sous-shell = contextes séparés | Éviter les sous-shells dans les boucles critiques |
À retenir
Section intitulée « À retenir »for item in "${tableau[@]}"est la forme sûre pour itérer sur un tableau, avec guillemets doubles.while IFS= read -r ligne; do ... done < fichierest le patron canonique pour lire un fichier ligne par ligne.- Évitez
cmd | while read, le pipe crée un sous-shell, les variables sont perdues après la boucle. breaksort de la boucle,continuepasse à l'itération suivante, suffixez d'un entier pour les boucles imbriquées.{1..N}ne s'expande pas avec des variables, utilisezseqoufor (( )).