Gestion des erreurs et debugging
Table des matières
- Codes de retour
- Options de sécurité
- Gestion des erreurs
- trap - Capturer les signaux
- Debugging
- Exercices pratiques
1 - Codes de retour
Comprendre $?
# Chaque commande retourne un code
ls /tmp
echo $? # 0 = succès
ls /nonexistent
echo $? # 2 = erreur
Codes conventionnels
| Code | Signification |
|---|---|
0 | Succès |
1 | Erreur générale |
2 | Mauvais usage |
126 | Permission refusée |
127 | Commande non trouvée |
128+N | Signal N reçu |
130 | Ctrl+C (SIGINT) |
137 | kill -9 (SIGKILL) |
Utiliser exit
#!/bin/bash
if [ $# -eq 0 ]; then
echo "Usage: $0 <fichier>" >&2
exit 1
fi
if [ ! -f "$1" ]; then
echo "Erreur: $1 n'existe pas" >&2
exit 2
fi
# Succès
exit 0
🔝 Retour à la table des matières
2 - Options de sécurité
set -e : Exit on error
#!/bin/bash
set -e # Arrête le script à la première erreur
mkdir /tmp/test
cd /tmp/test
rm -rf * # Si une erreur ici, le script s'arrête
set -u : Variables non définies
#!/bin/bash
set -u # Erreur si variable non définie
echo $UNDEFINED_VAR # Erreur immédiate
set -o pipefail : Erreurs dans les pipes
#!/bin/bash
set -o pipefail
# Sans pipefail: seul le dernier code est vérifié
false | true
echo $? # 0 (true a réussi)
# Avec pipefail: tout le pipe est vérifié
set -o pipefail
false | true
echo $? # 1 (false a échoué)
Combinaison recommandée
#!/bin/bash
set -euo pipefail
# Ou sur une ligne
set -euo pipefail
Désactiver temporairement
#!/bin/bash
set -e
# Commande qui peut échouer sans arrêter le script
set +e
command_qui_peut_echouer
result=$?
set -e
if [ $result -ne 0 ]; then
echo "La commande a échoué mais on continue"
fi
🔝 Retour à la table des matières
3 - Gestion des erreurs
Pattern : || et &&
# Exécuter si succès
mkdir /tmp/test && echo "Créé"
# Exécuter si échec
mkdir /tmp/test || echo "Échec"
# Combinaison
mkdir /tmp/test && echo "OK" || echo "FAIL"
Fonction die()
#!/bin/bash
die() {
echo "ERREUR: $*" >&2
exit 1
}
# Utilisation
[ -f "$1" ] || die "Fichier $1 non trouvé"
Vérifications complètes
#!/bin/bash
set -euo pipefail
# Fonction d'erreur
error() {
echo "[ERROR] $*" >&2
}
die() {
error "$@"
exit 1
}
# Vérifier les prérequis
check_requirements() {
command -v docker >/dev/null 2>&1 || die "Docker non installé"
command -v git >/dev/null 2>&1 || die "Git non installé"
[ -f "config.yml" ] || die "config.yml manquant"
}
# Vérifier les permissions
check_permissions() {
local dir="$1"
[ -w "$dir" ] || die "Pas de permission d'écriture sur $dir"
}
# Main
main() {
check_requirements
check_permissions "/tmp"
echo "Toutes les vérifications passées"
}
main "$@"
Try/Catch pattern
#!/bin/bash
try() {
"$@"
return $?
}
catch() {
if [ $? -ne 0 ]; then
"$@"
fi
}
# Utilisation
try rm fichier_inexistant 2>/dev/null
catch echo "Le fichier n'existait pas"
🔝 Retour à la table des matières
4 - trap - Capturer les signaux
Syntaxe
trap 'commandes' SIGNAL [SIGNAL...]
Signaux courants
| Signal | Numéro | Description |
|---|---|---|
EXIT | - | Sortie du script |
INT | 2 | Ctrl+C |
TERM | 15 | Terminaison |
ERR | - | Erreur (avec set -e) |
Nettoyage à la sortie
#!/bin/bash
TEMP_DIR=""
cleanup() {
echo "Nettoyage..."
[ -n "$TEMP_DIR" ] && rm -rf "$TEMP_DIR"
}
trap cleanup EXIT
# Créer un répertoire temporaire
TEMP_DIR=$(mktemp -d)
echo "Travail dans $TEMP_DIR"
# Le cleanup sera appelé automatiquement à la sortie
Gérer Ctrl+C
#!/bin/bash
interrupted=false
handle_interrupt() {
echo ""
echo "Interruption détectée, nettoyage..."
interrupted=true
}
trap handle_interrupt INT
while true; do
if $interrupted; then
echo "Arrêt propre"
break
fi
echo "Travail en cours..."
sleep 1
done
Trap sur erreur
#!/bin/bash
set -e
on_error() {
echo "Erreur à la ligne $1" >&2
}
trap 'on_error $LINENO' ERR
# Cette ligne va déclencher le trap
ls /nonexistent
Exemple complet
#!/bin/bash
set -euo pipefail
# Variables globales
TEMP_DIR=""
LOG_FILE="/tmp/script_$$.log"
# Fonction de nettoyage
cleanup() {
local exit_code=$?
echo "Nettoyage en cours..."
# Supprimer fichiers temporaires
[ -n "$TEMP_DIR" ] && rm -rf "$TEMP_DIR"
# Log final
if [ $exit_code -eq 0 ]; then
echo "Script terminé avec succès" >> "$LOG_FILE"
else
echo "Script terminé avec erreur (code: $exit_code)" >> "$LOG_FILE"
fi
exit $exit_code
}
# Enregistrer le trap
trap cleanup EXIT INT TERM
# Script principal
main() {
TEMP_DIR=$(mktemp -d)
echo "Démarrage du script"
# Votre logique ici
echo "Travail..."
sleep 2
echo "Script terminé"
}
main "$@"
🔝 Retour à la table des matières
5 - Debugging
set -x : Trace
#!/bin/bash
set -x # Active le mode trace
var="hello"
echo $var
# + var=hello
# + echo hello
# hello
Activer/désactiver localement
#!/bin/bash
echo "Partie normale"
set -x
# Cette partie est tracée
for i in 1 2 3; do
echo $i
done
set +x
echo "Retour à la normale"
Variables de debug
# Utiliser une variable DEBUG
DEBUG=${DEBUG:-false}
debug() {
if $DEBUG; then
echo "[DEBUG] $*" >&2
fi
}
debug "Variable x = $x"
# Lancer avec: DEBUG=true ./script.sh
Bashdb (debugger)
# Installer
sudo apt install bashdb
# Utiliser
bashdb script.sh
# Commandes bashdb:
# n - next (ligne suivante)
# s - step (entrer dans fonction)
# c - continue
# p $var - print variable
# b 10 - breakpoint ligne 10
Techniques de debug
#!/bin/bash
# 1. Afficher les variables
echo "DEBUG: var=$var" >&2
# 2. Afficher la pile d'appels
debug_trace() {
local i=0
while caller $i; do
((i++))
done
}
# 3. Vérifier le flux
echo "CHECKPOINT: Avant boucle" >&2
# 4. Utiliser PS4 personnalisé
export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
set -x
🔝 Retour à la table des matières
6 - Exercices pratiques
Exercice 1 : Script sécurisé
# Créez un script qui:
# - Utilise set -euo pipefail
# - Nettoie les fichiers temporaires à la sortie
# - Gère Ctrl+C proprement
Solution
#!/bin/bash
set -euo pipefail
TEMP_FILE=""
cleanup() {
[ -n "$TEMP_FILE" ] && rm -f "$TEMP_FILE"
echo "Nettoyage effectué"
}
trap cleanup EXIT INT TERM
TEMP_FILE=$(mktemp)
echo "Fichier temporaire: $TEMP_FILE"
# Simulation de travail
sleep 5
echo "Terminé"
Exercice 2 : Fonction avec gestion d'erreurs
# Créez une fonction qui télécharge un fichier
# avec gestion des erreurs (timeout, fichier existant, etc.)
Solution
download_file() {
local url="$1"
local dest="${2:-$(basename "$url")}"
local timeout="${3:-30}"
if [ -f "$dest" ]; then
echo "Fichier existe déjà: $dest" >&2
return 1
fi
if ! curl -fsSL --connect-timeout "$timeout" -o "$dest" "$url"; then
echo "Échec du téléchargement: $url" >&2
rm -f "$dest"
return 2
fi
echo "$dest"
return 0
}
Quiz
Q1. Que fait set -e ?
Réponse
Arrête le script immédiatement si une commande retourne un code non-zéro (erreur).
Q2. Comment capturer la sortie du script ?
Réponse
trap 'commandes' EXIT
Q3. Comment activer le mode trace pour debugger ?
Réponse
set -x ou bash -x script.sh
🔝 Retour à la table des matières
Points clés à retenir
set -euo pipefail: combo de sécurité recommandé$?contient le code de retour de la dernière commandeexit Npour quitter avec un code spécifiquetrappour capturer signaux et nettoyerset -xpour tracer l'exécution- Toujours valider les entrées et vérifier les prérequis
- Utiliser
>&2pour les messages d'erreur
🔝 Retour à la table des matières
← Chapitre précédent | Chapitre suivant : Bonnes pratiques →