Un script qui continue après une erreur ne gère pas les erreurs : il les ignore. Sans set -euo pipefail, un mv qui échoue affiche quand même "OK". Sans read -r, un backslash dans un nom de fichier disparaît silencieusement. Quelques lignes de protection suffisent à transformer un script fragile en script fiable en production.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Activer
set -euo pipefailpour arrêter le script dès la première erreur - Utiliser
read -ret citer les variables pour éviter le word splitting - Valider les entrées avec des tests
[[ ]]avant chaque action destructive - Intercepter les erreurs imprévues avec
trap - Créer des fichiers temporaires sécurisés avec
mktemp - Analyser et corriger un script avec shellcheck
Dans quel contexte ?
Section intitulée « Dans quel contexte ? »Un script fonctionnel en développement peut silencieusement échouer en production, là où les chemins diffèrent, les variables sont absentes, et personne ne regarde la sortie d'erreur :
- un script de sauvegarde qui continue après un
mvraté et marque l'opération comme réussie - un script de déploiement qui lit
$CONFIG_FILEvide parce que la variable n'a pas été exportée - un pipeline
cat log | grep ERR | wc -lqui retourne 0 même sicata échoué - un fichier temporaire prévisible dans
/tmpremplacé par un lien symbolique avant l'écriture - un mot de passe en clair dans un script versionné sur GitHub
Les techniques de ce guide s'appliquent à tout script destiné à tourner en production, en cron, ou dans une CI, dès que vous n'êtes pas là pour observer la sortie.
Un script sans protections, le problème
Section intitulée « Un script sans protections, le problème »Voici deplacement.sh, un script simple mais truffé de pièges :
#!/bin/bashecho "Fichier source :"read src
echo "Fichier destination :"read dst
mv $src $dstecho "Déplacement effectué."Quatre problèmes identifiables :
| Problème | Conséquence |
|---|---|
read sans -r | Les backslashes sont interprétés : \n devient un saut de ligne |
$src et $dst sans guillemets | Un espace dans le nom scinde l'argument en plusieurs mots |
| Pas de validation | Si le fichier source n'existe pas, mv échoue mais "Déplacement effectué." s'affiche quand même |
Pas de set -e | Un code de retour non nul ne stoppe pas l'exécution |
La suite corrige ces quatre problèmes.
set -euo pipefail
Section intitulée « set -euo pipefail »Placez toujours cette ligne juste après le shebang :
set -euo pipefail-e : s'arrêter à la première erreur
Section intitulée « -e : s'arrêter à la première erreur »Sans -e, un échec passe inaperçu :
#!/bin/bashrm /fichier/inexistantecho "Nettoyage terminé." # s'affiche quand mêmerm: cannot remove '/fichier/inexistant': No such file or directoryNettoyage terminé.Avec set -e, le script s'arrête après rm et n'affiche pas le message trompeur.
-u : interdire les variables non définies
Section intitulée « -u : interdire les variables non définies »set -uecho "Répertoire : $REPERTOIRE"bash: REPERTOIRE: unbound variableSans -u, la variable vide serait substituée silencieusement, potentiellement dangereux dans rm -rf "$dir/".
-o pipefail : détecter les erreurs dans les pipelines
Section intitulée « -o pipefail : détecter les erreurs dans les pipelines »Par défaut, seule la dernière commande d'un pipeline détermine le code de retour :
cat fichier_inexistant.txt | grep "motif"echo $? # retourne 1 (grep sans résultats), mais l'échec de cat est ignoréAvec set -o pipefail, le pipeline retourne le code du premier échec.
Valider et citer les variables
Section intitulée « Valider et citer les variables »read -r et guillemets doubles
Section intitulée « read -r et guillemets doubles »read -r préserve les backslashes tels quels. Les guillemets doubles protègent contre le word splitting et le globbing :
read -r src # -r : backslashes préservésmv "$src" "$dst" # guillemets : un seul argument même avec des espacesSans guillemets, si src="mon rapport.txt" alors mv $src $dst passe trois arguments à mv : mon, rapport.txt et la valeur de $dst.
Tester avant d'agir
Section intitulée « Tester avant d'agir »Validez chaque entrée avant toute opération destructive :
[[ -z "$src" ]] && erreur "Fichier source non spécifié."[[ -f "$src" ]] || erreur "Le fichier '$src' n'existe pas."
dst_dir=$(dirname "$dst")[[ -w "$dst_dir" ]] || erreur "Pas les droits d'écriture dans '$dst_dir'."Tests courants :
| Test | Signification |
|---|---|
-z "$var" | Variable vide ou non définie |
-n "$var" | Variable non vide |
-f "$chemin" | Fichier ordinaire existant |
-d "$chemin" | Répertoire existant |
-w "$chemin" | Accessible en écriture |
-r "$chemin" | Accessible en lecture |
trap, intercepter les signaux
Section intitulée « trap, intercepter les signaux »trap exécute une commande quand un signal ou un pseudo-signal est reçu.
Intercepter les erreurs inattendues (ERR)
Section intitulée « Intercepter les erreurs inattendues (ERR) »erreur() { echo "Erreur : $1" >&2 exit 1}
trap 'erreur "Interruption inattendue à la ligne $LINENO."' ERRQuand une commande échoue avec set -e actif, trap ERR s'exécute avant la sortie, utile pour afficher un message contextualisé avec le numéro de ligne.
Nettoyer à la sortie (EXIT)
Section intitulée « Nettoyer à la sortie (EXIT) »trap ... EXIT s'exécute toujours, quelle que soit la cause de la sortie (succès, erreur, Ctrl+C) :
tmpfile=$(mktemp)trap 'rm -f "$tmpfile"' EXIT
# Le fichier est supprimé même si le script échoue en cours de routemktemp, fichiers temporaires sécurisés
Section intitulée « mktemp, fichiers temporaires sécurisés »Ne jamais créer un fichier temporaire avec un nom prévisible (/tmp/monscript.tmp) : un attaquant peut créer un lien symbolique avant vous vers un fichier sensible.
tmpfile=$(mktemp) # Crée /tmp/tmp.xK3pQr (nom unique, permissions 600)tmprepertoire=$(mktemp -d) # Crée un répertoire temporaireAssocié à trap EXIT, la suppression est garantie même en cas d'erreur :
tmpfile=$(mktemp)trap 'rm -f "$tmpfile"' EXIT
grep "ERR\|WARN" /var/log/syslog > "$tmpfile"echo "$(wc -l < "$tmpfile") alertes trouvées"Pas de secrets en dur
Section intitulée « Pas de secrets en dur »Ne jamais coder un mot de passe ou une clé directement dans le script :
# À ne pas faireDB_PASSWORD="monsecret"mysql -u root -p"$DB_PASSWORD"Avec une variable d'environnement, définie avant l'appel :
# Dans le script : on lit la variable d'environnementmysql -u root -p"$DB_PASSWORD"# Avant d'exécuter le scriptexport DB_PASSWORD="monsecret"./mon_script.shAvec un fichier de configuration à accès restreint :
# config.conf : contient les secrets, jamais versionnéDB_PASSWORD="monsecret"chmod 600 config.confsource config.confmysql -u root -p"$DB_PASSWORD"Le script complet
Section intitulée « Le script complet »Voici deplacement_robuste.sh, version corrigée de deplacement.sh :
#!/bin/bashset -euo pipefail
# ── Gestion des erreurs ────────────────────────────────────────────────────────erreur() { echo "Erreur : $1" >&2 exit 1}
trap 'erreur "Interruption inattendue à la ligne $LINENO."' ERR
# ── Lectures sécurisées ────────────────────────────────────────────────────────echo "Fichier source :"read -r src
echo "Fichier destination :"read -r dst
# ── Validations ────────────────────────────────────────────────────────────────[[ -z "$src" ]] && erreur "Fichier source non spécifié."[[ -z "$dst" ]] && erreur "Fichier destination non spécifié."[[ -f "$src" ]] || erreur "Le fichier '$src' n'existe pas."
dst_dir=$(dirname "$dst")[[ -w "$dst_dir" ]] || erreur "Pas les droits d'écriture dans '$dst_dir'."
# ── Action ────────────────────────────────────────────────────────────────────mv -- "$src" "$dst"echo "OK : '$src' déplacé vers '$dst'."exit 0Le double tiret -- avant les arguments de mv protège contre les noms de fichiers commençant par -.
shellcheck et shfmt
Section intitulée « shellcheck et shfmt »shellcheck analyse statiquement un script et signale les mauvaises pratiques avant l'exécution :
sudo apt install shellcheck # Debian/Ubuntushellcheck deplacement.shIn deplacement.sh line 7:read src ^-- SC2162: read without -r will mangle backslashes.
In deplacement.sh line 13:mv $src $dst ^---^ SC2086: Double quote to prevent globbing and word splitting.Chaque code (SC2162, SC2086) pointe vers une explication détaillée sur shellcheck.net/wiki/SCxxxx.
shfmt reformate le code source de manière cohérente :
sudo apt install shfmtshfmt -i 4 -w mon_script.sh # indentation 4 espaces, réécriture en placeshfmt -d mon_script.sh # affiche les différences sans modifierDépannage
Section intitulée « Dépannage »| Symptôme | Cause probable | Solution |
|---|---|---|
| Le script s'arrête à une commande bénigne | -e interrompt sur tout code non nul | Protéger avec cmd || true ou if cmd; then ... fi |
unbound variable sur une variable initialisée | -u détecte un nom mal orthographié | Vérifier l'orthographe exacte et initialiser : var="" |
trap ERR ne se déclenche pas | -e non activé ou trap placé après l'erreur | Mettre set -euo pipefail et trap en tête de script |
mktemp: failed to create file | /tmp plein ou monté en readonly | df /tmp puis nettoyer les fichiers orphelins |
| Script fonctionne en interactif mais pas en cron | PATH différent dans cron | Utiliser les chemins complets (/bin/mv, /usr/bin/grep) |
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Profil de compétences
Quoi faire maintenant
Ressources pour progresser
Des indices pour retenter votre chance ?
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications
À retenir
Section intitulée « À retenir »set -euo pipefailest la première ligne après le shebang dans tout script de production.read -rpréserve les backslashes ; les guillemets doubles protègent contre le word splitting.- Valider toujours les entrées avant une opération destructive (
mv,rm,cp). trap 'cmd' ERRintercepte les erreurs inattendues ;trap 'cmd' EXITgarantit le nettoyage.mktempgénère un nom unique sécurisé, ne jamais utiliser/tmp/monscript.tmp.- shellcheck détecte la plupart des erreurs avant l'exécution, à lancer avant chaque commit.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Personnaliser Bash : Alias, prompt, fonctions et fichiers de configuration.
- Sudo et privilèges : Gérer les droits d'administration et le moindre privilège.