Error handling and debugging
Table of contents
1 - Return codes
Understanding $?
# Chaque commande retourne un code
ls /tmp
echo $? # 0 = succès
ls /nonexistent
echo $? # 2 = erreur
Conventional codes
| Code | Meaning |
|---|---|
0 | Success |
1 | General error |
2 | Misuse |
126 | Permission denied |
127 | Command not found |
128+N | Signal N received |
130 | Ctrl+C (SIGINT) |
137 | kill -9 (SIGKILL) |
Using 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
🔝 Back to table of contents
2 - Safety options
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: Undefined variables
#!/bin/bash
set -u # Erreur si variable non définie
echo $UNDEFINED_VAR # Erreur immédiate
set -o pipefail: Errors in 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é)
Recommended combination
#!/bin/bash
set -euo pipefail
# Ou sur une ligne
set -euo pipefail
Disable temporarily
#!/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
🔝 Back to table of contents
3 - Error handling
Pattern: || and &&
# 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"
The die() function
#!/bin/bash
die() {
echo "ERREUR: $*" >&2
exit 1
}
# Utilisation
[ -f "$1" ] || die "Fichier $1 non trouvé"
Complete checks
#!/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"
🔝 Back to table of contents
4 - trap - Catching signals
Syntax
trap 'commandes' SIGNAL [SIGNAL...]
Common signals
| Signal | Number | Description |
|---|---|---|
EXIT | - | Script exit |
INT | 2 | Ctrl+C |
TERM | 15 | Termination |
ERR | - | Error (with set -e) |
Cleanup on exit
#!/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
Handling 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 on error
#!/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
Complete example
#!/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 "$@"
🔝 Back to table of contents
5 - Debugging
set -x: Trace
#!/bin/bash
set -x # Active le mode trace
var="hello"
echo $var
# + var=hello
# + echo hello
# hello
Enable/disable locally
#!/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"
Debug variables
# 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
Debugging techniques
#!/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
🔝 Back to table of contents
6 - Practical exercises
Exercise 1: Secure script
# 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é"
Exercise 2: Function with error handling
# 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. What does set -e do?
Answer
Stops the script immediately if a command returns a non-zero (error) code.
Q2. How do you capture the script's exit?
Answer
trap 'commandes' EXIT
Q3. How do you enable trace mode for debugging?
Answer
set -x or bash -x script.sh
🔝 Back to table of contents
Key takeaways
set -euo pipefail: recommended safety combo$?holds the return code of the last commandexit Nto quit with a specific codetrapto catch signals and clean upset -xto trace execution- Always validate inputs and check prerequisites
- Use
>&2for error messages