Module 3 — Bonnes pratiques et pièges réels du scripting shell
Bonne lecture et bon apprentissage ! Junior TSAFACK – 12/09/2026 Temps de lecture estimé : 8 minutes
Le mode strict : set -euo pipefail
Section titled “Le mode strict : set -euo pipefail”Trois options qui devraient ouvrir tout script Bash destiné à tourner sans supervision humaine :
| Option | Effet |
|---|---|
set -e |
Arrête le script dès qu’une commande échoue (sans ça, Bash continue par défaut — un script qui échoue silencieusement à mi-chemin est le pire des scénarios en production). |
set -u |
Transforme l’utilisation d’une variable non définie en erreur, au lieu de la traiter silencieusement comme une chaîne vide. |
set -o pipefail |
Sans cette option, `commande_qui_echoue |
Gérer l’échec proprement avec trap
Section titled “Gérer l’échec proprement avec trap”set -e arrête le script, mais n’affiche rien d’utile sur où ni pourquoi. Un trap sur le signal ERR comble ce manque :
#!/usr/bin/env bashset -euo pipefailtrap 'echo "[ERREUR] la commande a échoué à la ligne $LINENO"; exit 1' ERR
echo "avant l'échec"commande-inexistante-xyzecho "jamais atteint"Exécution réelle :
$ ./trap_demo.sh ; echo "code de sortie : $?"avant l'échec[ERREUR] la commande a échoué à la ligne 6trap_demo.sh: line 6: commande-inexistante-xyz: command not foundcode de sortie : 1Le trap a intercepté l’échec, rapporté la ligne exacte (6, la ligne de la commande absente), puis le script s’est arrêté avec un code de sortie non nul — exactement l’information dont une alerte de supervision a besoin pour être exploitable, plutôt qu’un simple « le script a planté ».
Un vrai bug rencontré en écrivant ce cours : IFS casse $*
Section titled “Un vrai bug rencontré en écrivant ce cours : IFS casse $*”Un réflexe très répandu (recommandé dans beaucoup de guides « bonnes pratiques Bash ») est d’ajouter en tête de script :
IFS=$'\n\t'L’idée : empêcher les espaces dans les noms de fichiers de casser les boucles for non protégées. Ça part d’une bonne intention. Mais en écrivant le script « gabarit robuste » de ce cours, cette ligne a cassé une fonction de log qui fonctionnait très bien juste avant :
# Fonction de loglog() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"}
run() { log "[dry-run] $*" # $* doit joindre tous les arguments avec un espace}
run echo "Déploiement vers production"Sortie obtenue avec IFS=$'\n\t' en tête de script :
[2026-09-12 22:35:32] [dry-run] echoDéploiement vers productionSortie attendue (et obtenue après correction) :
[2026-09-12 22:36:26] [dry-run] echo Déploiement vers productionExplication : "$*" ne joint pas les mots avec un espace par convention fixe — il les joint avec le premier caractère de $IFS. Par défaut, ce premier caractère est un espace, donc "$*" se comporte comme attendu. Mais dès qu’on redéfinit IFS=$'\n\t', le premier caractère devient un retour à la ligne : chaque echo "[dry-run] $*" insère alors un saut de ligne entre les mots au lieu d’un espace, cassant silencieusement l’affichage (et potentiellement le parsing, si ce log était relu par un autre outil).
La leçon à en tirer : un idiome de « bonnes pratiques » copié sans le tester dans son propre script peut introduire un bug plus discret que le problème qu’il était censé prévenir. La correction ici a été de ne pas redéfinir IFS globalement : dans ce gabarit, toutes les boucles utilisent déjà des variables correctement guillemetées ("$file", "$@"), donc IFS=$'\n\t' n’apportait aucune protection supplémentaire — seulement un risque. La règle générale : ne redéfinissez IFS que localement, juste avant la boucle qui en a besoin, et remettez-le à sa valeur par défaut juste après (ou utilisez un sous-shell ( IFS=... ; ... ) pour limiter la portée du changement).
Autres pièges classiques (vérifiés dans les scripts de ce cours)
Section titled “Autres pièges classiques (vérifiés dans les scripts de ce cours)”- Oublier de guillemeter une variable :
rm -rf $DOSSIERest dangereux si$DOSSIERest vide (rm -rfsupprime alors le contenu du répertoire courant) — toujours écrirerm -rf "$DOSSIER", et idéalement vérifier[ -n "$DOSSIER" ]avant toute commande destructrice. - Confondre
$@et$*:"$@"préserve chaque argument comme un mot séparé (utile pour retransmettre des arguments à une autre commande, comme dans la fonctionrun()du Module 2) ;"$*"les fusionne en une seule chaîne (utile pour du texte à afficher). Les deux ont leur usage légitime — le bug arrive quand on les confond. - Ignorer le code de sortie d’un
cd: dans un script avecset -e, uncd /chemin/qui/nexiste/pasraté arrête heureusement le script — mais sansset -e, les commandes suivantes s’exécutent dans le mauvais répertoire, silencieusement. - Scripts non idempotents : un script d’automatisation relancé deux fois de suite doit produire le même résultat final, pas échouer ou dupliquer une ressource à la deuxième exécution (le script de sauvegarde du Module 2 est idempotent : le réexécuter crée juste une sauvegarde de plus, gérée par la rotation).
Prochaine étape
Section titled “Prochaine étape”Automatisation Python pour le DevOps — les mêmes idées (mode strict, gestion d’erreurs, idempotence) appliquées à des scripts Python, avec des tests unitaires réels.