Aller au contenu principal

Gestion des erreurs et debugging


Table des matières

  1. Codes de retour
  2. Options de sécurité
  3. Gestion des erreurs
  4. trap - Capturer les signaux
  5. Debugging
  6. 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

CodeSignification
0Succès
1Erreur générale
2Mauvais usage
126Permission refusée
127Commande non trouvée
128+NSignal N reçu
130Ctrl+C (SIGINT)
137kill -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

SignalNuméroDescription
EXIT-Sortie du script
INT2Ctrl+C
TERM15Terminaison
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 commande
  • exit N pour quitter avec un code spécifique
  • trap pour capturer signaux et nettoyer
  • set -x pour tracer l'exécution
  • Toujours valider les entrées et vérifier les prérequis
  • Utiliser >&2 pour les messages d'erreur

🔝 Retour à la table des matières


← Chapitre précédent | Chapitre suivant : Bonnes pratiques →