Scripts robustes#
Écrire un script Bash qui « fonctionne sur la machine du développeur » est relativement aisé. Écrire un script qui se comporte correctement dans un environnement de production — avec des entrées inattendues, des permissions manquantes, des disques pleins, des processus qui échouent en milieu de pipeline — est un tout autre défi. Ce chapitre présente les techniques et les conventions qui permettent de transformer un script fragile en un outil de confiance : gestion des erreurs, validation des entrées, nettoyage à la sortie et vérification syntaxique.
set -e : l’arrêt sur erreur#
Par défaut, Bash continue l’exécution même lorsqu’une commande renvoie un code de retour non nul. Ce comportement peut conduire à des situations catastrophiques : un script de déploiement qui échoue à compiler mais continue quand même à mettre en production un binaire inexistant, ou un script de sauvegarde qui rate la compression mais supprime quand même les fichiers d’origine.
```{prf:definition} L’option set -e
:label: definition-16-01
L’option set -e (ou son équivalent set -o errexit) demande à Bash de terminer immédiatement le script dès qu’une commande renvoie un code de retour non nul, sauf si cette commande fait partie d’une condition (if, while, until), d’une liste && ou ||, ou d’une liste de commandes dont la valeur n’est pas celle du script. C’est l’option la plus impactante pour la robustesse d’un script.
```bash
#!/usr/bin/env bash
set -e
echo "Étape 1 : compilation"
make build # Si make échoue, le script s'arrête ici
echo "Étape 2 : tests"
make test # Cette ligne ne s'exécute que si make build a réussi
echo "Étape 3 : déploiement"
rsync -av dist/ serveur:/var/www/
Sans set -e, si make build échoue, Bash affiche un message d’erreur mais continue et tente quand même de déployer. Avec set -e, l’arrêt est immédiat et les étapes suivantes sont court-circuitées.
Pièges et contournements#
set -e présente cependant plusieurs subtilités qui méritent d’être comprises avant une utilisation en production.
Piège 1 : les commandes dans une condition. Une commande qui fait partie d’une condition if n’est pas soumise à set -e, car son code de retour est précisément ce qui est testé.
set -e
# Correct : grep dans une condition n'arrête pas le script si rien n'est trouvé
if grep -q "erreur" fichier.log; then
echo "Des erreurs ont été trouvées."
fi
# Problème : grep utilisé hors condition, renvoie 1 si rien n'est trouvé
grep "erreur" fichier.log # Arrête le script si "erreur" est absent !
Piège 2 : les fonctions. Lorsqu’une fonction est utilisée dans une condition if, set -e ne s’applique pas aux commandes à l’intérieur de la fonction, ce qui peut conduire à des comportements inattendus.
set -e
verifier() {
false # Normalement fatal avec set -e
echo "Ceci s'affiche quand même si verifier() est dans un if"
}
if verifier; then # set -e est suspendu pour tout le corps de verifier()
echo "Succès"
fi
Piège 3 : les sous-shells. set -e se propage aux sous-shells, mais la valeur de retour du sous-shell est ce que voit le script parent.
Contournement courant : l’opérateur ||. Pour exécuter une commande qui peut légitimement échouer sans arrêter le script, on utilise || :
set -e
# Supprimer un fichier s'il existe, sans erreur s'il n'existe pas
rm fichier_temporaire.tmp || true
# Vérifier l'existence sans arrêter le script
if ! commande_qui_peut_echouer; then
echo "La commande a échoué, on gère l'erreur"
fi
Contournement avec affectation. L’affectation de la valeur de retour d’une commande via $() peut aussi contourner set -e selon la version de Bash :
set -e
# Méthode sûre : tester le code de retour explicitement
resultat=$(commande) || {
echo "La commande a échoué" >&2
exit 1
}
set -u : les variables non définies#
Une autre source courante de bugs silencieux est l’utilisation d’une variable non définie. Par défaut, Bash remplace une variable non définie par une chaîne vide, ce qui peut produire des effets dévastateurs :
# Sans set -u : rm -rf "$repertoire/" efface "/" si $repertoire est vide !
repertoire=""
rm -rf "$repertoire/" # Catastrophique !
```{prf:definition} L’option set -u
:label: definition-16-02
L’option set -u (ou set -o nounset) fait en sorte que Bash lève une erreur et termine le script dès qu’une variable non définie est référencée (sauf dans le cadre d’un test d’existence). C’est une protection essentielle contre les fautes de frappe dans les noms de variables et les cas où une variable n’a pas été initialisée correctement.
```bash
#!/usr/bin/env bash
set -u
echo "$VARIABLE_INEXISTANTE" # Erreur : "VARIABLE_INEXISTANTE: unbound variable"
Gérer les variables optionnelles avec ${var:-valeur}#
set -u pose problème pour les variables qui sont légitimement non définies, comme les paramètres optionnels d’un script. La solution est d’utiliser la syntaxe d’expansion avec valeur par défaut :
Exemple 29 (Syntaxes d’expansion pour les variables optionnelles)
Bash offre plusieurs formes d’expansion conditionnelle :
${variable:-valeur_par_defaut}: renvoievaleur_par_defautsivariableest non définie ou vide.${variable-valeur_par_defaut}: renvoievaleur_par_defautseulement sivariableest non définie (mais pas si elle est vide).${variable:=valeur_par_defaut}: comme:-mais assigne aussi la valeur par défaut àvariable.${variable:?message_erreur}: arrête le script avecmessage_erreursivariableest non définie ou vide.${variable:+valeur_alternative}: renvoievaleur_alternativesivariableest définie et non vide.
#!/usr/bin/env bash
set -u
# Paramètre optionnel avec valeur par défaut
ENVIRONNEMENT="${1:-production}"
TIMEOUT="${TIMEOUT:-30}"
# Paramètre obligatoire avec message d'erreur clair
FICHIER_CONFIG="${CONFIG_FILE:?La variable CONFIG_FILE doit être définie}"
# Vérifier si une variable est définie sans déclencher set -u
if [ -n "${VARIABLE_OPTIONNELLE:-}" ]; then
echo "La variable est définie et non vide : $VARIABLE_OPTIONNELLE"
fi
La syntaxe ${VARIABLE_OPTIONNELLE:-} avec une valeur par défaut vide est le moyen idiomatique de tester l’existence d’une variable sans déclencher set -u.
set -o pipefail : les erreurs dans les pipes#
Les pipes sont au cœur du shell Unix, mais ils présentent une subtilité fondamentale : par défaut, le code de retour d’un pipeline est uniquement celui de la dernière commande. Cela signifie qu’une erreur en milieu de pipeline est silencieusement ignorée.
```{prf:definition} L’option pipefail
:label: definition-16-03
L’option set -o pipefail modifie le comportement des pipelines : le code de retour d’un pipeline devient celui de la dernière commande ayant échoué, ou zéro si toutes les commandes ont réussi. Combinée avec set -e, elle garantit que le script s’arrête dès qu’une commande dans un pipeline échoue.
```bash
# Sans pipefail : ce pipeline renvoie 0 (succès de grep) même si cat échoue
cat fichier_inexistant.txt | grep "pattern"
echo "Code de retour : $?" # Affiche 0 !
# Avec pipefail : renvoie le code d'erreur de cat
set -o pipefail
cat fichier_inexistant.txt | grep "pattern"
echo "Code de retour : $?" # Affiche 1 (erreur de cat)
La combinaison canonique en tête de tout script robuste est donc :
#!/usr/bin/env bash
set -euo pipefail
Cette ligne unique active les trois protections fondamentales. Elle est devenue une convention de style largement partagée dans la communauté Bash.
La variable PIPESTATUS#
Lorsque l’on a besoin d’inspecter le code de retour de chaque commande d’un pipeline individuellement, Bash met à disposition le tableau PIPESTATUS :
commande1 | commande2 | commande3
echo "Codes : ${PIPESTATUS[@]}" # ex: "0 1 0" si commande2 a échoué
echo "Code de commande1 : ${PIPESTATUS[0]}"
echo "Code de commande2 : ${PIPESTATUS[1]}"
set -x : le mode débogage#
Lorsqu’un script se comporte de façon inattendue, le mode débogage est l’outil de première intention.
```{prf:definition} L’option set -x
:label: definition-16-04
L’option set -x (ou set -o xtrace) demande à Bash d”afficher chaque commande avant de l’exécuter, précédée du préfixe + (ou du contenu de la variable PS4). Les expansions de variables sont effectuées avant l’affichage, ce qui permet de voir exactement ce que Bash exécute réellement.
```bash
#!/usr/bin/env bash
set -x
FICHIER="/tmp/test.txt"
touch "$FICHIER"
echo "Contenu" > "$FICHIER"
Sortie :
+ FICHIER=/tmp/test.txt
+ touch /tmp/test.txt
+ echo Contenu
Activer et désactiver localement#
On peut activer et désactiver set -x à l’intérieur d’un script pour ne tracer que la section d’intérêt :
#!/usr/bin/env bash
set -euo pipefail
# ... code normal ...
set -x # Activer la trace
section_problematique
set +x # Désactiver la trace
# ... suite normale ...
Personnaliser le préfixe avec PS4#
La variable PS4 contrôle le préfixe affiché par set -x. Par défaut, c’est +, mais on peut l’enrichir pour inclure le numéro de ligne et le nom de la fonction :
export PS4='+ ${BASH_SOURCE[0]:-}:${LINENO} ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
set -x
Avec cette configuration, chaque ligne tracée indique le fichier source, le numéro de ligne et la fonction courante — une aide précieuse pour déboguer des scripts complexes avec plusieurs fichiers sources.
trap : gérer les signaux et nettoyer à la sortie#
Un script qui alloue des ressources — fichiers temporaires, connexions réseau, verrous — doit les libérer, même si le script est interrompu par un signal ou une erreur. L’instruction trap permet d’associer des actions à des événements.
```{prf:definition} L’instruction trap
:label: definition-16-05
trap permet d’exécuter une commande ou une fonction lorsque le shell reçoit un signal ou que certains pseudo-événements surviennent. La syntaxe est :
trap 'commande_ou_fonction' SIGNAL [SIGNAL...]
Les pseudo-événements les plus utiles sont :
EXIT: s’exécute à la fin du script, quelle que soit la cause (sortie normale,exit, erreur avecset -e, signal).ERR: s’exécute après chaque commande qui renvoie un code non nul.INT: correspond au signal SIGINT (Ctrl+C).TERM: correspond au signal SIGTERM (envoyé parkill).HUP: correspond au signal SIGHUP (fermeture du terminal).
### Le pattern de nettoyage à la sortie
Le pattern le plus courant est d'enregistrer une fonction de nettoyage sur l'événement `EXIT` en début de script :
```bash
#!/usr/bin/env bash
set -euo pipefail
# Créer un répertoire temporaire
TMPDIR=$(mktemp -d)
# Enregistrer le nettoyage AVANT toute utilisation de TMPDIR
nettoyage() {
echo "Nettoyage : suppression de $TMPDIR" >&2
rm -rf "$TMPDIR"
}
trap nettoyage EXIT
# À partir d'ici, TMPDIR sera toujours supprimé à la fin,
# que le script réussisse, échoue ou soit interrompu.
echo "Travail dans $TMPDIR"
cp fichier_source.txt "$TMPDIR/"
traiter "$TMPDIR/fichier_source.txt" > resultat.txt
Gestion des signaux interactifs#
Pour les scripts de longue durée, il est utile de gérer INT et TERM de façon explicite :
#!/usr/bin/env bash
set -euo pipefail
en_cours=true
traitement_signal() {
echo "Signal reçu, arrêt propre en cours..." >&2
en_cours=false
}
trap traitement_signal INT TERM
while $en_cours; do
effectuer_une_iteration
sleep 1
done
echo "Script terminé proprement."
trap '' SIGNAL pour ignorer un signal#
Pour qu’un signal soit ignoré (ce qui empêche la terminaison), on passe une chaîne vide comme commande :
trap '' INT # Ignorer Ctrl+C pendant une section critique
section_critique
trap - INT # Rétablir le comportement par défaut pour INT
Codes de retour : conventions et normalisation#
Chaque commande Unix renvoie un code de retour (aussi appelé exit status ou exit code) compris entre 0 et 255.
Définition 54 (Convention des codes de retour)
Par convention universelle dans les systèmes Unix :
0 signifie succès.
1 à 255 signifient erreur. La valeur précise est libre, mais certaines plages sont conventionnelles.
La commande exit N dans un script définit son code de retour. Sans exit explicite, le script renvoie le code de la dernière commande exécutée.
Remarque 43
Codes de retour normalisés selon la documentation Bash et les conventions POSIX :
0 : succès.
1 : erreur générique (usage courant pour les erreurs applicatives).
2 : mauvaise utilisation de la commande shell (arguments invalides).
126 : la commande a été trouvée mais n’est pas exécutable.
127 : commande introuvable.
128 : argument de
exitinvalide.128+N : signal N a provoqué la terminaison (ex. 130 = 128+2 pour SIGINT, soit Ctrl+C).
255 : code de retour hors de la plage 0-254.
#!/usr/bin/env bash
set -euo pipefail
# Utiliser des constantes nommées pour les codes d'erreur
readonly E_SUCCES=0
readonly E_ERREUR_GENERIQUE=1
readonly E_ARGS_INVALIDES=2
readonly E_FICHIER_ABSENT=3
readonly E_PERMISSION_REFUSEE=4
usage() {
echo "Usage : $0 [options] fichier" >&2
echo "Options :" >&2
echo " -v Mode verbeux" >&2
echo " -n Simulation (dry-run)" >&2
exit $E_ARGS_INVALIDES
}
if [ $# -eq 0 ]; then
usage
fi
Récupérer le code de retour de la commande précédente#
La variable spéciale $? contient toujours le code de retour de la dernière commande exécutée. Il est important de la lire immédiatement après la commande d’intérêt, car toute commande intermédiaire l’écrase :
commande_a_tester
code=$? # Sauvegarder immédiatement
if [ $code -ne 0 ]; then
echo "Échec avec le code $code" >&2
exit $code
fi
Validation des arguments#
Un script robuste commence par valider ses entrées avant d’effectuer quoi que ce soit d’irréversible.
```{prf:example} Fonction usage et validation des arguments
:label: example-16-02
Une structure typique de validation en début de script :
```bash
#!/usr/bin/env bash
set -euo pipefail
readonly SCRIPT_NOM="$(basename "$0")"
readonly SCRIPT_VERSION="1.0"
usage() {
cat <<EOF >&2
Usage : $SCRIPT_NOM [OPTIONS] FICHIER_SOURCE FICHIER_DEST
Copie et transforme FICHIER_SOURCE vers FICHIER_DEST.
OPTIONS :
-v, --verbose Afficher les détails de l'opération
-n, --dry-run Simuler sans modifier les fichiers
-h, --help Afficher cette aide
--version Afficher la version
EXEMPLES :
$SCRIPT_NOM -v /tmp/source.txt /tmp/dest.txt
$SCRIPT_NOM --dry-run fichier.csv sortie.csv
EOF
exit 2
}
erreur() {
echo "$SCRIPT_NOM: erreur : $*" >&2
exit 1
}
# --- Valeurs par défaut des options ---
VERBOSE=false
DRY_RUN=false
# --- Analyse des options avec getopts ---
while getopts ":vnh" opt; do
case $opt in
v) VERBOSE=true ;;
n) DRY_RUN=true ;;
h) usage ;;
:) erreur "L'option -$OPTARG requiert un argument." ;;
\?) erreur "Option inconnue : -$OPTARG" ;;
esac
done
shift $((OPTIND - 1))
# --- Vérification du nombre d'arguments ---
if [ $# -ne 2 ]; then
echo "$SCRIPT_NOM: erreur : exactement 2 arguments requis, $# fourni(s)." >&2
usage
fi
FICHIER_SOURCE="$1"
FICHIER_DEST="$2"
# --- Validation des valeurs ---
if [ ! -f "$FICHIER_SOURCE" ]; then
erreur "Le fichier source '$FICHIER_SOURCE' n'existe pas ou n'est pas un fichier régulier."
fi
if [ ! -r "$FICHIER_SOURCE" ]; then
erreur "Le fichier source '$FICHIER_SOURCE' n'est pas lisible."
fi
# Vérifier que le répertoire de destination existe
DEST_DIR="$(dirname "$FICHIER_DEST")"
if [ ! -d "$DEST_DIR" ]; then
erreur "Le répertoire de destination '$DEST_DIR' n'existe pas."
fi
if [ ! -w "$DEST_DIR" ]; then
erreur "Le répertoire de destination '$DEST_DIR' n'est pas accessible en écriture."
fi
Valider les types de données#
Pour les arguments qui doivent être des nombres, des dates ou respecter un format particulier, on peut utiliser des tests supplémentaires :
# Vérifier qu'un argument est un entier positif
valider_entier_positif() {
local valeur="$1"
local nom="$2"
if ! [[ "$valeur" =~ ^[0-9]+$ ]]; then
erreur "$nom doit être un entier positif, '$valeur' fourni."
fi
if [ "$valeur" -eq 0 ]; then
erreur "$nom doit être supérieur à zéro."
fi
}
# Vérifier qu'un argument est un chemin absolu
valider_chemin_absolu() {
local chemin="$1"
local nom="$2"
if [[ "$chemin" != /* ]]; then
erreur "$nom doit être un chemin absolu, '$chemin' fourni."
fi
}
# Vérifier qu'un argument appartient à un ensemble de valeurs autorisées
valider_choix() {
local valeur="$1"
local nom="$2"
shift 2
local -a choix=("$@")
local ok=false
for c in "${choix[@]}"; do
[ "$valeur" = "$c" ] && ok=true && break
done
if ! $ok; then
erreur "$nom doit être l'un de : ${choix[*]}. '$valeur' fourni."
fi
}
# Usage
TIMEOUT="${2:-30}"
valider_entier_positif "$TIMEOUT" "TIMEOUT"
NIVEAU="${3:-info}"
valider_choix "$NIVEAU" "NIVEAU" "debug" "info" "warning" "error"
Messages d’erreur vers stderr#
Une convention Unix fondamentale : les messages d’erreur et d’avertissement doivent être envoyés vers la sortie d’erreur standard (stderr, descripteur 2), et non vers la sortie standard (stdout, descripteur 1). Cela permet aux programmes appelants de séparer les données utiles des messages de diagnostic.
Définition 55 (Redirection vers stderr)
La redirection >&2 envoie la sortie de la commande vers le descripteur de fichier 2 (stderr). Toute commande peut voir sa sortie redirigée :
echo « Message d’erreur » >&2 printf « Erreur : %s\n » « $message » >&2
Remarque 44
La distinction stdout/stderr est cruciale dans les pipelines et les scripts. Lorsqu’on écrit script.sh | autre_commande, seule la sortie standard de script.sh est transmise à autre_commande. Les messages d’erreur envoyés sur stderr s’affichent directement dans le terminal sans polluer le pipeline. De même, resultat=$(script.sh) capture uniquement stdout ; les messages d’erreur de script.sh s’affichent quand même dans le terminal.
Structure recommandée pour les fonctions de messages :
#!/usr/bin/env bash
set -euo pipefail
readonly SCRIPT_NOM="$(basename "$0")"
# Niveaux de log
log_info() { echo "[$SCRIPT_NOM] INFO : $*" >&2; }
log_warning() { echo "[$SCRIPT_NOM] WARNING : $*" >&2; }
log_erreur() { echo "[$SCRIPT_NOM] ERREUR : $*" >&2; }
log_fatal() { echo "[$SCRIPT_NOM] FATAL : $*" >&2; exit 1; }
# Optionnel : colorer les messages si le terminal le supporte
if [ -t 2 ] && [ "$(tput colors 2>/dev/null || echo 0)" -ge 8 ]; then
readonly ROUGE='\033[0;31m'
readonly JAUNE='\033[1;33m'
readonly VERT='\033[0;32m'
readonly RESET='\033[0m'
log_info() { printf "${VERT}[INFO]${RESET} %s\n" "$*" >&2; }
log_warning() { printf "${JAUNE}[WARNING]${RESET} %s\n" "$*" >&2; }
log_erreur() { printf "${ROUGE}[ERREUR]${RESET} %s\n" "$*" >&2; }
log_fatal() { printf "${ROUGE}[FATAL]${RESET} %s\n" "$*" >&2; exit 1; }
fi
# Usage
log_info "Démarrage du script"
log_warning "Le fichier de configuration par défaut est utilisé"
log_erreur "Impossible de se connecter à la base de données"
log_fatal "Erreur irrécupérable, arrêt du script"
bash -n : vérification syntaxique sans exécution#
Avant de déployer un script, il est prudent de vérifier sa syntaxe sans l’exécuter.
```{prf:definition} bash -n
:label: definition-16-08
L’option bash -n (no execute) lit et analyse le script sans exécuter aucune commande. Elle signale les erreurs de syntaxe — accolades non fermées, if sans fi, etc. — mais ne détecte pas les erreurs sémantiques comme les variables non définies ou les commandes inexistantes.
```bash
# Vérifier la syntaxe d'un script
bash -n mon_script.sh
# Vérifier plusieurs scripts
bash -n *.sh
# Intégrer dans un workflow de vérification
if bash -n "$SCRIPT"; then
echo "Syntaxe correcte"
else
echo "Erreurs de syntaxe détectées" >&2
exit 1
fi
Remarque 45
bash -n vérifie uniquement la syntaxe Bash. Pour une analyse plus complète incluant les pièges courants de style et de sécurité, on utilisera ShellCheck (voir le chapitre 20). bash -n est néanmoins utile dans les hooks Git pre-commit pour rejeter rapidement les scripts syntaxiquement invalides.
Structure complète d’un script robuste#
Voici un exemple rassemblant toutes les bonnes pratiques présentées dans ce chapitre :
#!/usr/bin/env bash
# =============================================================================
# nom_script.sh — Description courte
#
# Usage : nom_script.sh [OPTIONS] ARG1 ARG2
#
# Description longue du script, de ses effets, de ses dépendances.
#
# Options :
# -v Mode verbeux
# -n Simulation sans modification
# -h Afficher l'aide
#
# Codes de retour :
# 0 Succès
# 1 Erreur générique
# 2 Arguments invalides
# 3 Fichier introuvable
#
# Auteur : Prénom Nom <email@exemple.com>
# Version : 1.0.0
# Date : 2024-01-15
# =============================================================================
set -euo pipefail
IFS=$'\n\t' # Séparateur interne : sauts de ligne et tabulations uniquement
# =============================================================================
# Constantes et configuration
# =============================================================================
readonly SCRIPT_NOM="$(basename "$0")"
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_VERSION="1.0.0"
readonly E_SUCCES=0
readonly E_ERREUR=1
readonly E_ARGS=2
readonly E_FICHIER=3
# =============================================================================
# Fonctions utilitaires
# =============================================================================
usage() {
cat <<EOF >&2
Usage : $SCRIPT_NOM [OPTIONS] ARG1 ARG2
...
EOF
exit $E_ARGS
}
log() { echo "[$SCRIPT_NOM] $*" >&2; }
erreur() { log "ERREUR : $*"; exit $E_ERREUR; }
# =============================================================================
# Nettoyage à la sortie
# =============================================================================
TMPDIR_TRAVAIL=""
nettoyage() {
local code=$?
if [ -n "$TMPDIR_TRAVAIL" ] && [ -d "$TMPDIR_TRAVAIL" ]; then
rm -rf "$TMPDIR_TRAVAIL"
log "Répertoire temporaire supprimé."
fi
exit $code
}
trap nettoyage EXIT
trap 'erreur "Signal INT reçu"' INT
trap 'erreur "Signal TERM reçu"' TERM
# =============================================================================
# Analyse des options
# =============================================================================
VERBOSE=false
DRY_RUN=false
while getopts ":vnh" opt; do
case $opt in
v) VERBOSE=true ;;
n) DRY_RUN=true ;;
h) usage ;;
:) erreur "L'option -$OPTARG requiert un argument." ;;
\?) erreur "Option inconnue : -$OPTARG" ;;
esac
done
shift $((OPTIND - 1))
# =============================================================================
# Validation des arguments
# =============================================================================
[ $# -ge 2 ] || { log "ERREUR : au moins 2 arguments requis."; usage; }
ARG1="$1"
ARG2="$2"
[ -f "$ARG1" ] || erreur "Fichier introuvable : '$ARG1'"
[ -r "$ARG1" ] || erreur "Fichier non lisible : '$ARG1'"
# =============================================================================
# Corps principal
# =============================================================================
main() {
TMPDIR_TRAVAIL="$(mktemp -d)"
log "Répertoire de travail : $TMPDIR_TRAVAIL"
$VERBOSE && log "Mode verbeux activé."
$DRY_RUN && log "Mode simulation : aucune modification ne sera effectuée."
# ... logique métier ...
log "Traitement terminé avec succès."
}
main "$@"
Résumé#
Ce chapitre a présenté les fondations de l’écriture de scripts Bash fiables :
La combinaison
set -euo pipefailest le point de départ de tout script robuste : arrêt sur erreur, détection des variables non définies et propagation des erreurs dans les pipelines.set -xetPS4personnalisé permettent de déboguer avec précision, etset +xlocalise la trace aux sections d’intérêt.trapavec l’événementEXITgarantit l’exécution du nettoyage (fichiers temporaires, verrous) quelles que soient les circonstances de terminaison.Les codes de retour suivent une convention universelle (0 = succès, 1–255 = erreur) et la variable
$?doit être lue immédiatement après la commande d’intérêt.La validation des arguments — nombre, type, format, permissions — doit précéder toute opération irréversible.
Les messages d’erreur appartiennent à stderr (
>&2), les données utiles à stdout : cette séparation est indispensable pour la composition par pipelines.bash -noffre une vérification syntaxique rapide, à compléter par ShellCheck pour une analyse sémantique complète.
Dans le chapitre suivant, nous abordons les expressions régulières avancées — BRE, ERE et PCRE — et leur utilisation avec grep, sed et awk pour des transformations de texte puissantes.