Stratégies de bouclage puissantes : itérer sur des fichiers et des listes dans les scripts Bash
Maîtrisez les techniques essentielles de bouclage Bash avec `for` et `while` pour automatiser efficacement les tâches système répétitives. Ce guide complet couvre l'itération sur des listes, le traitement de séquences numériques et la gestion robuste de fichiers ligne par ligne en utilisant les bonnes pratiques comme `while IFS= read -r`. Apprenez la syntaxe fondamentale, le contrôle avancé des boucles (`break`, `continue`) et les techniques essentielles pour des scripts shell puissants et fiables, le tout accompagné d'exemples de code pratiques.
Stratégies de bouclage puissantes : itérer sur des fichiers et des listes dans les scripts Bash
Les boucles Bash transforment de petites commandes shell en automatisation utile. Que vous ayez besoin de traiter chaque fichier d'un répertoire, d'effectuer une tâche un nombre défini de fois ou de lire des données de configuration ligne par ligne, les boucles vous offrent la structure nécessaire pour répéter un travail sans copier-coller de commandes.
Les deux boucles que vous utiliserez le plus sont for et while. Utilisez for lorsque vous avez déjà un ensemble connu d'éléments, comme un tableau ou un glob de fichiers. Utilisez while lorsque la boucle est pilotée par une condition ou par la lecture d'une entrée. Cette simple distinction rend de nombreux scripts plus faciles à raisonner.
La boucle for : itérer sur des ensembles fixes
La boucle for est idéale lorsque vous connaissez à l'avance la collection d'éléments à traiter. Cette collection peut être une liste explicite de valeurs, les résultats d'une commande ou un ensemble de fichiers trouvés via un glob.
1. Itération sur des listes standard
Le cas d'utilisation le plus simple est l'itération sur une courte liste de mots écrits directement dans le script.
Syntaxe
for VARIABLE in LISTE_D_ELEMENTS; do
# Commandes utilisant $VARIABLE
done
Exemple : Traitement d'une liste d'utilisateurs
# Liste des utilisateurs à traiter
UTILISATEURS="alice bob charlie"
for utilisateur in $UTILISATEURS; do
echo "Vérification du répertoire personnel pour $utilisateur..."
if [ -d "/home/$utilisateur" ]; then
echo "$utilisateur est actif."
else
echo "Attention : le répertoire personnel de $utilisateur est manquant."
fi
done
Ce modèle convient pour des noms simples. Si un élément peut contenir des espaces, utilisez un tableau au lieu d'une chaîne séparée par des espaces :
UTILISATEURS=("alice" "bob" "mary jane")
for utilisateur in "${UTILISATEURS[@]}"; do
echo "Vérification de $utilisateur"
done
2. Itération numérique de style C
Pour les tâches nécessitant un comptage ou des séquences numériques spécifiques, Bash prend en charge une boucle for de style C, souvent combinée avec l'expansion d'accolades ou la commande seq.
Syntaxe (style C)
for (( INITIALISATION; CONDITION; INCREMENT )); do
# Commandes
done
Exemple : Script de compte à rebours
# Boucle 5 fois (i commence à 1, continue tant que i est inférieur ou égal à 5)
for (( i=1; i<=5; i++ )); do
echo "Numéro d'itération : $i"
sleep 1
done
echo "Terminé !"
Alternative : Utilisation de l'expansion d'accolades pour des séquences simples
L'expansion d'accolades est plus simple et plus rapide que l'utilisation de seq pour générer des entiers contigus ou des séquences.
# Génère les nombres de 10 à 1
for num in {10..1}; do
echo "Compte à rebours : $num"
done
3. Itération sur des fichiers et répertoires (glob)
L'utilisation de caractères génériques (*) dans la boucle for vous permet de traiter les fichiers correspondant à un motif spécifique, comme tous les fichiers journaux ou tous les scripts d'un répertoire.
Exemple : Archivage de fichiers journaux
Mettez la variable entre guillemets ("$fichier") lorsque vous traitez des noms de fichiers, en particulier ceux contenant des espaces ou des caractères spéciaux.
REPERTOIRE_CIBLE="/var/log/application"
# Boucle sur tous les fichiers se terminant par .log dans le répertoire cible
for fichier_log in "$REPERTOIRE_CIBLE"/*.log; do
# Vérifie si un fichier existe réellement (empêche l'exécution sur le littéral "*.log" si aucun fichier ne correspond)
if [ -f "$fichier_log" ]; then
echo "Compression de $fichier_log..."
gzip "$fichier_log"
fi
done
La boucle while : exécution basée sur une condition
La boucle while continue d'exécuter un bloc de commandes tant qu'une condition spécifiée reste vraie. Elle est couramment utilisée pour lire des flux d'entrée, surveiller des conditions ou gérer des tâches où le nombre d'itérations est inconnu.
1. Boucle while de base
Syntaxe
while CONDITION; do
# Commandes
done
Exemple : Attente d'une ressource
Cette boucle utilise la commande test ([ ]) pour vérifier si un répertoire existe avant de continuer.
CHEMIN_RESSOURCE="/mnt/data/partage"
while [ ! -d "$CHEMIN_RESSOURCE" ]; do
echo "Attente du montage de la ressource $CHEMIN_RESSOURCE..."
sleep 5
done
echo "La ressource est disponible. Démarrage de la sauvegarde."
2. Le modèle robuste while read
L'application la plus puissante de la boucle while est la lecture du contenu d'un fichier ou d'un flux de sortie ligne par ligne. Ce modèle est bien supérieur à l'utilisation d'une boucle for sur la sortie de cat, car il gère de manière fiable les espaces et les caractères spéciaux.
Bonne pratique : Lecture ligne par ligne
Pour garantir une robustesse maximale, nous utilisons trois composants clés :
IFS=: Efface le séparateur de champ interne, garantissant que la ligne entière, y compris les espaces de début et de fin, est lue dans la variable.read -r: L'option-rempêche l'interprétation des barres obliques inverses (lecture brute), ce qui est essentiel pour les chemins et les chaînes complexes.- Redirection d'entrée (
<) : Redirige le contenu du fichier dans la boucle, garantissant que la boucle s'exécute dans le contexte du shell actuel (empêchant les problèmes de sous-shell).
# Fichier contenant des données, un élément par ligne
FICHIER_CONFIG="/etc/app/serveurs.txt"
while IFS= read -r nom_serveur; do
# Ignorer les lignes vides ou commentées
if [[ -z "$nom_serveur" || "$nom_serveur" =~ ^# ]]; then
continue
fi
echo "Ping du serveur : $nom_serveur"
ping -c 1 "$nom_serveur"
done < "$FICHIER_CONFIG"
Astuce : Éviter
catdans les bouclesPréférez
while ... done < fichieràcat fichier | while ...lors de la lecture d'un fichier. Dans la plupart des configurations Bash, un pipeline exécute la boucle dans un sous-shell, donc les variables modifiées à l'intérieur de la boucle sont perdues lorsque la boucle se termine.
3. Gestion des noms de fichiers provenant de find
Pour le traitement récursif de fichiers, évitez d'analyser la sortie brute de find ligne par ligne. Les noms de fichiers peuvent contenir des espaces et, rarement, des sauts de ligne. Utilisez une sortie délimitée par des caractères nuls :
find /var/log/application -type f -name '*.log' -print0 |
while IFS= read -r -d '' fichier_log; do
echo "Fichier journal trouvé : $fichier_log"
gzip -- "$fichier_log"
done
La paire -print0 et read -d '' traite l'octet nul comme séparateur. Le -- avant "$fichier_log" indique à gzip que les valeurs suivantes sont des opérandes, et non des options, ce qui vous protège des noms de fichiers commençant par -.
Contrôle avancé des boucles et techniques
Des scripts efficaces nécessitent la capacité de contrôler l'exécution des boucles en fonction des conditions d'exécution.
1. Contrôle du flux : break et continue
break: Quitte immédiatement la boucle entière, quels que soient les itérations ou conditions restantes.continue: Ignore l'itération en cours et passe immédiatement à l'itération suivante (ou réévalue la conditionwhile).
Exemple : Recherche et arrêt
CIBLE_RECHERCHE="target.conf"
for fichier in /etc/*; do
if [ -f "$fichier" ] && [[ "$fichier" == *"$CIBLE_RECHERCHE"* ]]; then
echo "Configuration cible trouvée à : $fichier"
break # Arrêter le traitement une fois trouvé
elif [ -d "$fichier" ]; then
continue # Ignorer les répertoires, ne vérifier que les fichiers
fi
echo "Vérification du fichier : $fichier"
done
2. Gestion des délimiteurs complexes avec IFS
Bien que la lecture de fichiers ligne par ligne nécessite d'effacer IFS, l'itération sur une liste séparée par un caractère différent (comme une virgule) nécessite de définir temporairement IFS.
DONNEES_CSV="donnee1,donnee2,donnee3,donnee4"
ANCIEN_IFS=$IFS # Sauvegarder l'IFS d'origine
IFS=',' # Définir IFS sur le caractère virgule
for element in $DONNEES_CSV; do
echo "Élément trouvé : $element"
done
IFS=$ANCIEN_IFS # Restaurer l'IFS d'origine immédiatement après la boucle
Avertissement : Modifications globales de
IFSSauvegardez toujours le
$IFSd'origine avant de le modifier dans un script (par exemple,ANCIEN_IFS=$IFS). Ne pas restaurer la valeur d'origine peut entraîner un comportement imprévisible dans les commandes suivantes.
Bonnes pratiques pour des boucles Bash robustes
| Pratique | Justification |
|---|---|
| Toujours mettre les variables entre guillemets | Utilisez "$variable" pour éviter la division des mots et l'expansion des globs, en particulier dans l'itération de fichiers. |
Utiliser while IFS= read -r |
La méthode la plus fiable pour traiter les fichiers ligne par ligne, gérant correctement les espaces et les caractères spéciaux. |
| Vérifier l'existence | Lors de l'utilisation de globs (*.txt), incluez toujours une vérification (if [ -f "$fichier" ];) pour garantir que la boucle ne traite pas le nom de motif littéral si aucun fichier ne correspond. |
| Localiser les variables | Utilisez le mot-clé local dans les fonctions pour empêcher les variables de boucle d'écraser accidentellement les variables globales. |
| Utiliser les commandes internes plutôt que les commandes externes | Utilisez l'expansion d'accolades ({1..10}) ou les boucles de style C plutôt que de lancer des commandes externes comme seq pour les performances. |
Une règle empirique pratique
Utilisez des tableaux pour les listes en mémoire, des globs pour les ensembles de fichiers simples, while IFS= read -r pour les entrées orientées ligne, et la sortie find délimitée par des caractères nuls pour la gestion récursive des noms de fichiers. Mettez les expansions entre guillemets par défaut. Ajoutez des vérifications d'existence autour des globs. Réservez break et continue aux cas où ils rendent la boucle plus facile à lire, et non comme un moyen de masquer un flux de contrôle compliqué.
La plupart des bugs de boucle Bash proviennent de la division des mots, de noms de fichiers inattendus ou de l'hypothèse que l'entrée est plus propre qu'elle ne l'est. Si votre boucle gère délibérément les espaces, les lignes vides, les commentaires et les correspondances manquantes, elle survivra au travail d'automatisation réel.