Bash : bonnes pratiques
Quelques habitudes évitent la plupart des problèmes d’un script, surtout quand il tourne sans surveillance (cron, déploiement). Les exemples utilisent des noms génériques.
Squelette de départ
Section intitulée « Squelette de départ »#!/usr/bin/env bashset -euo pipefail
# --- Configuration ---DOSSIER="${1:?Usage: $0 <dossier>}"
# --- Fonctions ---log() { echo "$(date '+%F %T') $*"; }
# --- Programme principal ---log "Début"# ...log "Fin"Mode strict : set -euo pipefail
Section intitulée « Mode strict : set -euo pipefail »| Option | Effet |
|---|---|
-e |
Le script s’arrête dès qu’une commande échoue |
-u |
Utiliser une variable non définie est une erreur (évite rm -rf "$DOSSIER/" avec DOSSIER vide) |
-o pipefail |
Un pipe échoue si une de ses commandes échoue, pas seulement la dernière |
Limites à connaître :
# grep renvoie 1 quand rien ne correspond : avec set -e, le script s'arrêteraitnb="$(grep -c "motif" fichier || true)" # « || true » : ignorer l'échec
# Une commande dans un if ou avec && / || ne déclenche pas set -eif grep -q "motif" fichier; then echo "trouvé"; fiAutre piège avec pipefail : head ferme le pipe dès qu’il a assez de lignes, et la commande en amont reçoit alors le signal SIGPIPE (code 141), ce qui fait échouer le pipe et arrêter le script :
sort -rn grosse-liste.txt | head -n 3 # peut échouer (code 141) avec set -e -o pipefailsort -rn grosse-liste.txt | awk 'NR <= 3' # awk lit tout : pas de SIGPIPEToujours mettre des guillemets
Section intitulée « Toujours mettre des guillemets »cp "$source" "$destination" # correct, même avec des espacesrm -rf "${DOSSIER:?}/"* # « :? » refuse une variable videVérifier avant d’agir
Section intitulée « Vérifier avant d’agir »# Être root si nécessaireif [[ $EUID -ne 0 ]]; then echo "Ce script doit être lancé avec sudo" >&2 exit 1fi
# Commande disponible ?command -v rsync >/dev/null || { echo "rsync est requis" >&2; exit 1; }
# Fichier ou dossier présent ?[[ -d "$DOSSIER" ]] || { echo "Dossier introuvable : $DOSSIER" >&2; exit 1; }Nettoyer avec trap
Section intitulée « Nettoyer avec trap »trap exécute une commande quand le script se termine, même en cas d’erreur ou d’interruption (Ctrl+c) :
tmp="$(mktemp -d)" # dossier temporaire uniquetrap 'rm -rf "$tmp"' EXIT # supprimé à la fin, quoi qu'il arrive
echo "données" > "$tmp/fichier"mktemp crée un nom de fichier ou de dossier unique : ne pas utiliser un nom fixe dans /tmp (risque de collision et de sécurité).
Éviter l’exécution simultanée
Section intitulée « Éviter l’exécution simultanée »exec 9> /tmp/mon-script.lockflock -n 9 || { echo "Une autre instance est en cours" >&2; exit 1; }Journaliser
Section intitulée « Journaliser »LOG="/var/log/mon-script.log"exec >> "$LOG" 2>&1 # tout ce qui suit est écrit dans le journalecho "$(date '+%F %T') Démarrage"Pour une sortie à la fois à l’écran et dans un fichier : ./script.sh 2>&1 | tee -a script.log.
Faire des scripts idempotents
Section intitulée « Faire des scripts idempotents »Un script idempotent peut être relancé sans dommage :
mkdir -p /srv/donnees # n'échoue pas si le dossier existegrep -qxF "ligne" fichier || echo "ligne" >> fichier # ajoute la ligne seulement si absenteln -sfn /srv/v2 /srv/courant # remplace le lien s'il existeVérifier et contrôler le script
Section intitulée « Vérifier et contrôler le script »sudo apt install shellcheckshellcheck script.sh # détecte guillemets manquants, erreurs courantes et piègesbash -n script.sh # vérifie seulement la syntaxeShellCheck est l’outil le plus utile pour progresser : chaque avertissement est expliqué (SC2086 : variable sans guillemets, par exemple).
Scripts lancés par cron
Section intitulée « Scripts lancés par cron »Cron s’exécute avec un environnement minimal : ni les variables ni le PATH habituels.
crontab -e# Sauvegarde tous les jours à 3 h, journal des erreurs conservé0 3 * * * /usr/local/bin/sauvegarde.sh >> /var/log/sauvegarde.log 2>&1- Utiliser des chemins absolus pour les commandes et les fichiers.
- Rediriger la sortie vers un journal (sans quoi elle part par courrier local).
- Tester d’abord le script à la main, avec
env -i bash script.shpour simuler un environnement vide.
Organisation et lisibilité
Section intitulée « Organisation et lisibilité »- Un nom de script explicite (
sauvegarde-dossier.sh), une brève description en commentaire et un message d’usage. - Les valeurs modifiables en haut du script, en majuscules.
- Des fonctions courtes ;
localpour leurs variables. - Un script de plus de quelques centaines de lignes, ou avec des structures de données complexes : envisager un langage plus adapté (Python).
Ubuntu 24.04 et 26.04 : différences à connaître
Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »Pour aller plus loin
Section intitulée « Pour aller plus loin »- ShellCheck : shellcheck.net
- Manuel :
man bash, section « SHELL BUILTIN COMMANDS »