Meilleures pratiques de scripting Bash pour une automatisation fiable.
Écrivez des scripts Bash plus sûrs avec le mode strict, des guillemets soigneux, des pièges de nettoyage, la validation et des habitudes de débogage pratiques.
Meilleures pratiques de script Bash pour une automatisation fiable
L'écriture de scripts Bash est souvent le pilier de l'automatisation système, des pipelines DevOps et des tâches administratives courantes. Une petite erreur de guillemets ou un code de sortie ignoré peut supprimer les mauvais fichiers, masquer un déploiement échoué ou laisser des tâches de nettoyage en suspens.
Ces meilleures pratiques de script Bash se concentrent sur les habitudes qui rendent l'automatisation plus sûre : mode strict, gestion minutieuse des variables, pièges de nettoyage, fonctions lisibles et tests simples avant d'exécuter des commandes destructrices.
1. Établir une base solide : Gestion des erreurs
L'aspect le plus critique d'un script Bash fiable est une gestion appropriée des erreurs. Par défaut, Bash est permissif ; il continue souvent l'exécution même après l'échec d'une commande. Ce comportement doit être explicitement remplacé pour garantir un échec immédiat en cas d'erreur.
La règle d'or : La commande set
Chaque script Bash non trivial devrait commencer par activer le mode strict à l'aide de la commande set. Cette seule ligne augmente considérablement la fiabilité de votre code.
#!/usr/bin/env bash
set -euo pipefail
Signification des indicateurs :
-e(errexit) : Quitte immédiatement si une commande se termine avec un statut non nul. Cela empêche la poursuite silencieuse après un échec. Exception : Les commandes dans les conditionsif,whileouuntil, ou les commandes précédées de!.-u(nounset) : Traite les variables et paramètres non définis comme une erreur. Cela détecte les fautes de frappe et les erreurs logiques où une variable était censée être définie.-o pipefail: Si une commande dans un pipeline échoue, le statut de sortie de l'ensemble du pipeline est celui de la dernière commande ayant échoué, plutôt que le statut de sortie de la dernière commande du pipeline (qui pourrait réussir même si une étape antérieure a échoué).
Gestion du nettoyage des scripts avec les pièges (traps)
La commande trap vous permet d'exécuter des commandes lorsque des signaux spécifiques sont reçus (par exemple, interruptions, sorties ou erreurs). Ceci est crucial pour nettoyer les fichiers temporaires ou les ressources, même si le script échoue de manière inattendue.
# Définir le chemin du répertoire temporaire
TMP_DIR=$(mktemp -d)
# Fonction pour nettoyer le répertoire temporaire
cleanup() {
if [[ -d "$TMP_DIR" ]]; then
rm -rf "$TMP_DIR"
echo "Répertoire temporaire nettoyé : $TMP_DIR"
fi
}
# Exécuter la fonction de nettoyage lorsque le script se termine (0, 1, 2, etc.) ou est interrompu (SIGINT)
trap cleanup EXIT HUP INT QUIT TERM
# Exemple d'utilisation du répertoire temporaire
echo "Travail dans $TMP_DIR"
# ... logique du script ...
2. Prévenir les pièges : Guillemets et variables
La source la plus courante de comportement imprévisible dans Bash est l'utilisation incorrecte des guillemets pour les variables.
Toujours mettre les variables entre guillemets
Chaque fois que vous utilisez une variable qui est développée dans un argument de commande, toujours l'entourer de guillemets doubles ("$VARIABLE"). Cela empêche le découpage de mots et le globing (expansion des noms de chemin), surtout si la variable contient des espaces ou des caractères spéciaux.
La différence de guillemets
| Scénario | Commande | Résultat |
|---|---|---|
| Sans guillemets (Mauvais) | rm $FILE_LIST |
Si $FILE_LIST contient "file one.txt", rm voit deux arguments : file et one.txt. |
| Avec guillemets (Bon) | rm "$FILE_LIST" |
Si $FILE_LIST contient "file one.txt", rm voit un argument : file one.txt. |
Utiliser des accolades pour plus de clarté
Utilisez des accolades ({}) lors du développement de variables pour délimiter clairement le nom de la variable du texte environnant, ou pour accéder en toute sécurité aux éléments d'un tableau.
LOG_FILE="backup_$(date +%Y%m%d).log"
echo "Journalisation dans : ${LOG_FILE}"
Préférer les variables locales dans les fonctions
Lors de la définition de variables à l'intérieur d'une fonction, utilisez le mot-clé local pour garantir qu'elles n'écrasent pas accidentellement les variables globales, réduisant ainsi les effets secondaires et améliorant la modularité.
process_data() {
local input_data="$1"
local processed_count=0
# ... logique ...
}
3. Meilleures pratiques structurelles et maintenabilité
Les scripts bien structurés sont plus faciles à déboguer, tester et maintenir dans le temps.
Modulariser la logique avec des fonctions
Utilisez des fonctions pour décomposer les tâches complexes en blocs plus petits et réutilisables. Les fonctions imposent une meilleure séparation des préoccupations et améliorent considérablement la lisibilité du script.
check_prerequisites() {
if ! command -v git &> /dev/null; then
echo "Erreur : Git est requis mais n'est pas installé." >&2
exit 1
fi
}
main() {
check_prerequisites
# ... logique principale du script ...
}
# L'exécution commence ici
main "$@"
Utiliser des noms descriptifs et des commentaires
- Variables : Utilisez
UPPER_CASEpour les constantes globales (ou variables de configuration) etsnake_caseoulower_casepour les variables locales. Soyez explicite (par exemple,TOTAL_RECORDSau lieu deT). - Commentaires : Utilisez des commentaires pour expliquer le pourquoi derrière une logique complexe, pas seulement le quoi. Incluez un bloc d'en-tête complet détaillant le but du script, son utilisation, son auteur et sa version.
Validation des entrées et gestion des arguments
Validez toujours les entrées utilisateur, en vous assurant que le nombre requis d'arguments est fourni et que ces arguments sont au format attendu.
#!/usr/bin/env bash
set -euo pipefail
# Vérifier si le nombre correct d'arguments est fourni
if [[ $# -ne 2 ]]; then
echo "Utilisation : $0 <chemin_source> <chemin_destination>" >&2
exit 1
fi
SRC="$1"
DEST="$2"
# Vérifier si le chemin source existe et est lisible
if [[ ! -d "$SRC" ]]; then
echo "Erreur : Le répertoire source '$SRC' est introuvable." >&2
exit 1
fi
4. Portabilité et sélection du shell
Lors du choix de votre shell et de vos commandes, tenez compte de qui exécutera le script et où.
Choisir un shebang spécifique
Utilisez la ligne shebang (#!) pour déclarer explicitement l'interpréteur. L'utilisation de /usr/bin/env bash est souvent préférée à /bin/bash car elle permet au système de trouver le bon exécutable bash en fonction du PATH de l'utilisateur.
- Si vous avez besoin de fonctionnalités avancées (tableaux, syntaxe moderne, mathématiques strictes), utilisez :
#!/usr/bin/env bash - Si vous avez besoin d'une portabilité maximale sur les systèmes Unix (en évitant les fonctionnalités spécifiques à Bash), utilisez :
#!/bin/sh(Remarque :/bin/shest souvent lié àdashou à un shell minimal sur de nombreux systèmes Linux).
Éviter les utilitaires non standard
Dans la mesure du possible, tenez-vous-en aux utilitaires standard POSIX. Si vous avez besoin de fonctionnalités avancées, documentez clairement la dépendance externe.
| Éviter (Non standard) | Préférer (Standard/Courant) |
|---|---|
gdate (BSD/macOS) |
date |
Extensions GNU sed |
Syntaxe sed standard |
Expressions régulières en ligne (=~ dans Bash) |
Outils externes comme grep ou awk |
Utiliser [[ ... ]] plutôt que [ ... ] dans les scripts Bash
Bash fournit la construction conditionnelle [[ ... ]] (souvent appelée nouvelle syntaxe de test), qui est généralement plus sûre et plus puissante que la traditionnelle [ ... ] (la commande test POSIX standard).
[[ ... ]]réduit les surprises de découpage de mots dans les tests, bien que la mise entre guillemets des variables reste une bonne habitude par défaut.- Elle prend en charge des fonctionnalités puissantes comme la correspondance de motifs (
==,!=) et la correspondance regex (=~).
5. Meilleures pratiques de débogage et de test
Des tests approfondis sont essentiels pour une automatisation fiable.
Tester tôt et souvent
Utilisez des fonctions petites et atomiques qui peuvent être testées individuellement. Écrivez des tests unitaires si la complexité le justifie (des outils comme Bats ou ShellSpec sont excellents pour cela).
Utiliser les indicateurs de débogage
Pour le débogage interactif, vous pouvez activer des indicateurs spécifiques pendant l'exécution :
- Activer le traçage verbeux (
-x) : Affiche les commandes et leurs arguments au fur et à mesure de leur exécution, précédés de+.
bash -x votre_script.sh
# Ou ajoutez cette ligne temporairement dans votre script :
# set -x
- Activer les vérifications à blanc (
-n) : Lit les commandes mais ne les exécute pas. Utile pour les vérifications de syntaxe avant d'exécuter un script complexe ou destructeur.
bash -n votre_script.sh
Assurer la vérification du statut de sortie
Lors de l'appel de programmes externes, vérifiez toujours leur statut de sortie si vous n'utilisez pas set -e. Utilisez $? immédiatement après la commande pour capturer son statut.
copy_files data/* /tmp/backup
if [[ $? -ne 0 ]]; then
echo "La copie de fichiers a échoué !" >&2
exit 1
fi
À retenir
L'automatisation fiable avec Bash repose sur une base de normes d'exécution strictes, une structure soignée et une programmation défensive. En appliquant systématiquement set -euo pipefail, en mettant toujours vos variables entre guillemets, en utilisant des fonctions pour la modularité et en effectuant les validations d'entrée nécessaires, vous garantissez que vos scripts échouent rapidement, échouent en toute sécurité et sont facilement maintenables pour de futures améliorations ou dépannages.