Scripting en Bash avec set -euo pipefail, trap et logging

Dernière mise à jour: 17/12/2025
Auteur: Isaac
  • L'activation de `set -euo pipefail` et de safe IFS permet aux scripts bash d'échouer plus tôt et d'éviter les erreurs silencieuses lors de la gestion des codes de sortie et des variables non définies.
  • L'utilisation de pièges pour les erreurs ERR et les signaux tels que INT ou TERM facilite l'enregistrement des défauts, le nettoyage contrôlé et le maintien du système dans un état cohérent.
  • Un système de journalisation centralisé avec niveaux et horodatages améliore la traçabilité, le débogage et l'intégration des scripts dans les environnements DevOps et CI/CD.
  • La combinaison d'une programmation défensive, de la validation des entrées et de bonnes pratiques de débogage rend les scripts bash fiables en production.

Que sont les blobs binaires ou propriétaires sous Linux ?

L'automatisation des tâches avec Bash fait toute la différence entre un système nécessitant une surveillance constante et un système qui fonctionne de manière autonome et vous alerte en cas de problème . Dans les environnements DevOps et d'administration système, un bon script ne se limite pas à l'enchaînement de commandes : il s'agit de prévenir les pannes silencieuses, de faciliter le débogage rapide et de laisser des journaux d'activité clairs pour comprendre ce qui s'est passé et quand.

Lorsqu'on travaille sérieusement avec des scripts, on découvre que Bash est, par défaut, assez permissif : en cas d'erreur, le script continue généralement comme si de rien n'était, en utilisant des variables vides ou des résultats incomplets . Cela peut mener à des situations catastrophiques comme des sauvegardes tronquées (pour lesquelles il est conseillé de synchroniser avec rsync sous Linux ), des suppressions massives dues à des chemins d'accès mal construits, ou des déploiements qui semblaient corrects mais qui ont échoué en cours de route. C'est pourquoi il est essentiel d'apprendre à utiliser correctement `set -euo`, `pipefail`, `trap` et un système de journalisation efficace.

Pourquoi Bash reste un élément clé du DevOps

Dans le travail quotidien des systèmes et du DevOps, Bash reste l'outil incontournable : sur les serveurs Linux , les conteneurs, les machines CI/CD, les scripts de maintenance… Son plus grand avantage est qu'il ne nécessite pas l'installation d'environnements d'exécution ou de bibliothèques externes ; avec le shell fourni avec le système, vous pouvez tout automatiser, des contrôles de santé aux déploiements complets.

Ce pouvoir a un revers : avec des scripts mal écrits, il est bien trop facile qu’une commande échoue tandis que le reste du script continue comme si de rien n’était . Ajoutez à cela des tâches critiques (sauvegardes, rotation des journaux, suppression de fichiers, exécutions planifiées), et le risque devient concret : tôt ou tard, quelque chose dysfonctionnera sans même que vous vous en aperceviez.

C'est pourquoi il est essentiel, en plus de l'automatisation, d'intégrer dès le départ la gestion des erreurs, le débogage et une journalisation structurée . Trois éléments, présents dans tous les bons exemples de scripts Bash modernes, entrent alors en jeu : `set -euo pipefail`, `trap` et un système de journalisation cohérent.

Exemple pratique : script de surveillance avec gestion stricte des erreurs

Une pratique très utile consiste à créer un script de surveillance qui collecte les informations système essentielles (CPU, mémoire, disque, réseau, processus, journaux récents, etc.) et qui gère les pannes de manière sécurisée, en laissant des traces claires dans un fichier journal . Ce type de script peut être utilisé manuellement, via cron ou intégré à des pipelines CI/CD.

Idée générale L'idée générale est d'avoir un script avec :

  • Mode strict activé Utilisez `set -euo pipefail` pour échouer prématurément et ne pas continuer à fonctionner dans des états incohérents.
  • piège sur ERR et signaux comme INT ou TERM consigner les erreurs et nettoyer avant de partir.
  • Fonction de journalisation centralisée qui écrit dans la console un fichier avec un horodatage et un niveau (INFO, WARNING, ERROR).
  • Bilans de santé modulaires Journaux du processeur, de la mémoire, du disque, du réseau et du système.
  • Installation optionnelle des dépendances tels que sysstat, lm-sensors ou net-tools, adaptés à Debian/Ubuntu ou RHEL/CentOS/Fedora.

Dans un script de ce type, il est courant de voir quelque chose comme :

  • Variables de couleur pour mettre en évidence les avertissements et les erreurs dans le terminal (ROUGE, VERT, JAUNE, BLEU, NC).
  • Des itinéraires comme /var/log/system-monitor.log pour les journaux généraux et /var/log/system-metrics.log pour les indicateurs périodiques.
  • Seuils configurables alerte : par exemple, ALERT_CPU_THRESHOLD=80, ALERT_MEM_THRESHOLD=80, ALERT_DISK_THRESHOLD=85.
  • Un intervalle de surveillance MONITOR_INTERVAL permet de répéter les vérifications toutes les X secondes si vous exécutez le script en boucle.

Sur cette base, un script robuste est construit qui non seulement collecte des informations, mais réagit également correctement aux défaillances, laisse des preuves claires et peut être intégré dans des processus plus vastes.

Activer le mode strict : définir -euo pipefail et sécuriser IFS

La plupart des problèmes « étranges » de Bash proviennent du fait que, par défaut, le shell ne considère pas comme grave l'échec d'une commande ou l'absence d'une variable . Pour imposer un comportement plus sûr, on active souvent ce que beaucoup appellent le « mode Bash strict ».

  Comment diffuser du contenu de Windows 11 sur une Smart TV

Les composantes de base de ce mode strict sont :

  • définir -e: provoque l'arrêt du script dès qu'une commande renvoie un code de sortie différent de 0, sauf dans certains contextes particuliers (if, while, &&, ||…).
  • définir -u o set -o substantif: provoque une erreur lorsque vous tentez d'utiliser une variable non définie, au lieu de la traiter comme une chaîne vide.
  • set -o échec du tuyau: fait en sorte que le code de sortie dans un pipeline soit celui de la première commande qui échoue, et non pas seulement celui de la dernière.
  • IFS=$'\n\t': limite le séparateur de champs interne au saut de ligne et à la tabulation pour éviter que les espaces ne perturbent les boucles sur des listes de fichiers ou similaires.

La combinaison de tous ces éléments aboutit à quelque chose de très courant dans l'en-tête des scripts sérieux :

set -euo pipefail
IFS=$'\n\t'
tête de lit sécurisée

Cela évite les situations où une commande intermédiaire dans un pipeline échoue, mais que le script continue car la dernière commande renvoie 0. Cela empêche également les variables mal orthographiées ou non définies de devenir silencieusement des chaînes vides, ce qui est souvent la source de chemins incorrects ou de conditions logiques qui ne sont jamais remplies.

Les pièges de l'option `set -e` : pourquoi elle n'est pas magique et peut endommager votre script

Bien que très utile, `set -e` n'est pas une solution miracle . Son comportement présente des nuances qui peuvent prêter à confusion si vous n'en comprenez pas le fonctionnement. Par exemple, certaines constructions comme :

  • Commandes à l'intérieur if, while o until.
  • Expressions avec && y || pour le contrôle du débit.
  • Certains développements arithmétiques.

Elles ne déclenchent pas la « sortie immédiate » comme on pourrait s'y attendre. Un exemple classique est le développement arithmétique avec post-incrémentation :

((contador++))

L'expansion arithmétique renvoie une valeur que Bash interprète comme un code de sortie. Dans le cas de l'incrémentation, si l'expression renvoie 0, l'opération est considérée comme réussie. En revanche, elle peut parfois renvoyer 1, ce qui, avec ` set -e`, provoque l'arrêt immédiat du script . Un élément en apparence aussi anodin qu'un compteur dans une boucle peut donc entraîner l'arrêt prématuré de votre script.

En revanche, des constructions comme ((++i)) ou ((i+=1)) se comportent différemment et ne provoquent pas le même problème. Cela montre qu'avec `set -e`, il est essentiel de bien maîtriser les constructions utilisées , car de nombreux cas limites existent.

Par conséquent, plusieurs auteurs et administrateurs expérimentés recommandent d'utiliser set -e en particulier dans la phase de test , comme un moyen agressif de trouver les points faibles, mais en production, ils préfèrent une approche plus explicite : vérifier manuellement les codes de sortie et décider exactement quand et comment interrompre le script.

piège : capturer les erreurs et les signaux pour nettoyer et consigner correctement

La commande `trap` est l'autre élément clé lorsqu'on parle de scripts défensifs en Bash. Elle permet de spécifier les commandes ou fonctions à exécuter lorsque le shell reçoit un signal ou un événement particulier (comme ERR ou EXIT).

Utilisations typiques Les utilisations typiques de trap dans les scripts robustes sont les suivantes :

  • piège 'error_handling_function' ERR: exécuter la logique de gestion des erreurs dès qu'une commande échoue.
  • piège 'cleaning_function' INT TERM: réagir aux signaux tels que CTRL+C (SIGINT) ou aux requêtes d'arrêt (SIGTERM).
  • piège 'finalization_function' SORTIE: exécuter un bloc à la sortie du script, qu'il se soit bien déroulé ou non.

Imaginez un script qui, lorsqu'il reçoit CTRL+C, au lieu de s'arrêter brutalement, appelle une fonction de nettoyage qui supprime les fichiers temporaires, ferme les connexions ou laisse un message clair dans les journaux :

trap 'limpieza' TERM INT
function limpieza(){
echo "Ejecutando limpieza, el usuario uso CTRL + C"
# ... lógica de limpieza ...
}
action préventive

Ce modèle est particulièrement utile dans les processus longs (sauvegardes, ETL, déploiements) où vous souhaitez pouvoir interrompre de manière contrôlée sans laisser le système dans un état incohérent.

Ce que vous pouvez et ne pouvez pas capturer avec un piège ERR

Exemple courant : De nombreux guides suggèrent d’utiliser quelque chose comme :

trap 'echo "Error en línea $LINENO" >&2' ERR ligne de capture

pour consigner les défauts avec leur numéro de ligne. C'est bien, mais il est important d'en comprendre les limites . Trap peut :

  • Exécuter du code supplémentaire lorsqu'une erreur est déclenchée.Par exemple, écrire dans un journal avec la date, le script et la ligne.
  • Accédez au code de sortie de la dernière commande avec $?.
  • savoir la ligne de script avec $LINENO, très utile pour le débogage.
  Trucs et astuces pour maîtriser Honor of Kings

Le gestionnaire d'erreurs ERR ne peut pas récupérer la sortie d'erreur standard (stderr) d'une commande déjà exécutée, sauf si vous l'avez préalablement redirigée vers un fichier ou un descripteur que vous pouvez ensuite lire. L'erreur elle-même est un flux de texte, et non une valeur unique.

Certaines méthodes consistent à rediriger la sortie d'erreur standard (stderr) de l'ensemble du script vers un journal temporaire et, dans le bloc d'interruption, à ajouter un en-tête contenant la date, le fichier et le numéro de ligne afin de pouvoir associer le bloc d'erreur à l'endroit précis du script où la défaillance s'est produite. C'est plus de travail, mais en cas de plantage en production, on apprécie d'avoir le contexte.

Journalisation dans Bash : enregistrer uniquement ce qui est nécessaire, formaté et sans encombrer le système.

Même pour automatiser des tâches minimalement critiques, il est essentiel de savoir ce qui s'est passé, quand et quel en a été le résultat . C'est pourquoi la quasi-totalité des exemples de scripts sérieux intègrent une fonction de journalisation centralisée afin d'uniformiser le format.

Une fonction typique pourrait ressembler à ceci :

log_message() {
local level="$1"
local message="$2"
local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
echo -e "${timestamp} ${message}" | tee -a "$LOG_FILE"
}
format unifié

Exemples : Avec un outil comme celui-ci, vous pouvez appeler :

  • log_message "INFO" "Démarrage de la vérification du processeur"
  • log_message "AVERTISSEMENT" "Utilisation du disque supérieure à 85%"
  • log_message "ERREUR" "Impossible de se connecter à la base de données"

Veillez à toujours conserver le même format, avec la date et le niveau clairement indiqués. L'utilisation de `tee -a` permet également d'afficher le message à l'écran et de l'enregistrer simultanément dans un fichier journal, ce qui est très pratique pour une utilisation interactive et une analyse ultérieure.

Une autre bonne pratique consiste à séparer les journaux d'activité (ce que fait le script) et les journaux de métriques ou de résultats (utilisation du processeur, de la RAM, du disque, etc.) dans des fichiers différents. Par exemple :

  • /var/log/system-monitor.log pour les événements et les messages.
  • /var/log/system-metrics.log pour les relevés d'état périodiques.

Cela facilite l'intégration. Il devient ainsi plus aisé d'alimenter des outils externes, des analyseurs syntaxiques ou même des solutions d'observabilité sans mélanger les messages humains avec les données machine.

Vérification de l'état du système : processeur, mémoire, disque, réseau et journaux

Dans les environnements Linux, l'utilisation de Bash est très courante pour implémenter un script de « vérification système rapide » qui recueille les informations de base nécessaires au diagnostic immédiat des problèmes . Ce type de script comprend généralement plusieurs blocs :

  • charge du processeur: résistant mpstat si elle est disponible (grâce au paquet sysstat), ou en recourant à top en mode batch pour obtenir un résumé de l'utilisation du processeur.
  • Processus les plus utilisés par processeur: avec ps aux --sort=-%cpu | head -n N pour voir ce qui consomme le plus de ressources.
  • Espace disque: avec df -h, en accordant une attention particulière à la partition racine et à tous les volumes critiques pour les applications ou bases de données (voir comment libérer de l'espace sous Linux).
  • Mémoire RAM et mémoire d'échange: à travers de free -hpour voir quelle quantité est utilisée et si une utilisation excessive de la mémoire d'échange est constatée.
  • État du réseau: vérification de la connectivité externe par un ping vers une adresse IP connue (généralement 8.8.8.8) et liste des interfaces actives avec ip addr showet en utilisant des outils tels que netcat (nc/ncat) pour des tests plus avancés.
  • Journaux système récents: tracer les dernières lignes avec journalctl -n ou équivalent, pour détecter les erreurs de service juste avant l'exécution du script.

Le résultat est généralement enregistré dans un fichier journal ou affiché de manière formatée à l'écran, offrant un aperçu rapide de l'état du système que vous pouvez consulter vous-même ou intégrer à des outils de surveillance.

Installation automatique des dépendances et vérification des privilèges

Prérequis Un détail important de nombreux scripts d'administration est qu'avant toute action importante, ils vérifient que deux conditions sont remplies :

  • Le script s'exécute en tant que racine (ou par le sud).
  • Les outils nécessaires sont installés selon le système d'exploitation.

vérification de la racine La vérification de la racine se fait généralement avec $EUID:

check_root() {
if ; then
log_message "ERROR" "Este script necesita privilegios de root para instalar paquetes."
log_message "WARNING" "Por favor, ejecute con sudo."
exit 1
fi
}
Cela échoue si vous n'avez pas les droits root.

De cette manière, vous évitez les erreurs ultérieures dues à des autorisations insuffisantes et vous envoyez un message clair à l'utilisateur dès le départ.

  Comment supprimer un module complémentaire de Microsoft Edge

détecter la distribution L'installation des dépendances est généralement prise en charge par /etc/os-release pour détecter la famille de distribution :

  • En Debian / Ubuntu S'utilise apt-get pour installer des paquets tels que sysstat, lm-sensors, net-tools.
  • En CentOS / RHEL / Fedora serait tiré dnf o yum avec des paquets équivalents (lm_sensors, net-tools, etc.).

Alertes, tâches cron et exportation des journaux : mise en production du script

Une fois que vous disposez d'un script de vérification ou d'automatisation relativement robuste, l'étape logique suivante consiste à l'intégrer à votre routine quotidienne :

  • Alertes par e-mail, Slack ou autres canauxPar exemple, si l'utilisation du disque dépasse 90 %, vous envoyez un e-mail via mailx ou un webhook Slack. Généralement, on vérifie la valeur avec df/awk/sed et, si elle dépasse un certain seuil, on déclenche la notification.
  • Exécution périodique avec cron: ajoutez une entrée comme ceci 0 8 * * * /ruta/a/script.sh pour qu'il s'exécute tous les jours à une heure précise.
  • Redirection de la sortie vers les fichiers journaux: exécuter ./script.sh > /var/log/system-check.log 2>&1 si vous souhaitez consigner toutes les sorties standard et d'erreur dans un seul fichier.

Il est important de comprendre que si votre script est conçu pour s'arrêter à la première erreur à l'aide de `set -euo pipefail`, cela signifie que la tâche cron ou le pipeline CI/CD qui l'appelle verra clairement un code de sortie différent de zéro en cas d'échec, ce qui vous permettra de déclencher des flux de récupération, de marquer les builds comme ayant échoué, etc.

Débogage en Bash : définir les options -x, -v, -ny tracer dans un fichier

Bash ne possède pas de débogueur comme certains autres langages, mais il offre plusieurs options de débogage très puissantes qui, lorsqu'elles sont utilisées correctement, permettent d'économiser de nombreuses heures à se demander « pourquoi diable cela se produit-il ? » :

  • définir -x o définir -o xtraceAffiche chaque commande exécutée avec tous ses paramètres déjà développés. Idéal pour visualiser les valeurs réelles des variables.
  • définir -v o set -o verbeuxAffiche les lignes de script au fur et à mesure de leur lecture, même avant leur développement. Utile pour comprendre le flux d'exécution.
  • définir -n o définir -o noexec: analyse la syntaxe sans exécuter le code, idéal pour détecter les guillemets, accolades ou crochets manquants.
  • définir -uComme mentionné précédemment, il échoue lorsque des variables non définies sont utilisées, ce qui est très utile lors du débogage d'erreurs logiques.

De plus, vous pouvez limiter l'effet de ces options à un fragment spécifique du script en l'encadrant par `set -x` / `set +x`, par exemple :

set -x
# bloque problemático
read -p "Pass Dir name : " D_OBJECT
read -p "Pass File name : " F_OBJECT
set +x
trace temporelle

#!/bin/bash
exec 6> salida_debug.log
BASH_XTRACEFD="6"
set -x
# ... resto del script ...
trace de redirection

Ainsi, toutes les informations de débogage sont enregistrées dans le fichier output_debug.log , et vous ne verrez plus que la sortie « normale » du script dans le terminal.

Exemple typique de bug lié à des variables non définies

Un problème très fréquent lorsqu'on n'utilise pas `set -u` est l'invocation involontaire de variables inexistantes . Imaginez un script qui demande le nom d'un objet, puis vérifie s'il s'agit d'un fichier ou d'un répertoire, mais qui utilise un nom de variable incorrect :

#!/bin/bash
read -p "Nombre del Objeto : " OBJECT
if ]; then
echo "$OBJECT es un archivo"
elif ]; then
echo "$OBJECT es un directorio"
fi
erreur variable

Comme $OBJECT1 n'est pas défini , Bash le remplace par une chaîne vide ; les tests -fy -d ne renvoient pas d'erreur, ils échouent simplement, et le script se termine avec le code 0. Rien ne « se passe », mais logiquement, le résultat n'est pas celui attendu.

Si vous exécutez le script avec `bash -u nom_du_script` ou `set -u` , vous obtiendrez immédiatement une erreur « Variable non définie », vous conduisant directement à l'erreur de type. Combiné à `bash -x nom_du_script` ou `set -x`, vous verrez également la ligne et les valeurs utilisées, ce qui facilite grandement le débogage.

Créer des conteneurs légers avec Podman sous Linux
Article connexe:
Conteneurs légers avec Podman sous Linux : guide pratique