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 (( )).