Un script bash tué en pleine exécution (Ctrl-C, un arrêt systemd, un pod Kubernetes qui reçoit un SIGTERM) laisse souvent derrière lui un fichier temporaire, un lockfile, ou une ressource non libérée, sauf si le script intercepte explicitement le signal avant que l’interruption ne survienne.
Ce que trap fait réellement
trap associe une commande ou une fonction à un signal donné : quand ce signal arrive, bash exécute cette commande avant de continuer (ou d’arrêter) l’exécution normale du script. Sans trap, un signal comme SIGTERM ou SIGINT termine le script immédiatement, sans laisser la moindre chance à un nettoyage.
#!/usr/bin/env bash
LOCKFILE=/tmp/mon-script.lock
touch "$LOCKFILE"
# Nettoie le lockfile quel que soit le signal reçu,
# avant que le script ne se termine réellement
trap 'rm -f "$LOCKFILE"' SIGTERM SIGINT EXIT
# ... traitement long ...
sleep 300
EXIT : le pseudo-signal qu’on oublie souvent
EXIT n’est pas un vrai signal Unix, mais bash le traite comme tel dans trap : la commande associée s’exécute à chaque sortie du script, qu’elle soit normale (fin du script) ou provoquée par un signal intercepté par ailleurs. Un trap ... EXIT seul suffit donc souvent à couvrir le nettoyage sans lister individuellement chaque signal possible, à condition que le signal en question ne soit pas de ceux qu’on ne peut pas intercepter.
Ce qu’on ne peut jamais intercepter
SIGKILL (kill -9) et SIGSTOP ne peuvent être interceptés par aucun trap, une limitation du kernel plutôt que de bash : ces deux signaux terminent ou suspendent le processus directement, sans lui laisser la moindre opportunité d’exécuter du code, y compris un nettoyage pourtant défini. Un lockfile qui persiste après un kill -9 n’est donc pas un bug du script : c’est le comportement attendu et documenté de ce signal précis.
# Aucun trap ne peut intercepter ceci,
# le nettoyage ne s'exécutera jamais
kill -9 $PID
Où placer le trap dans le script
Un trap défini après la création d’une ressource (le lockfile, dans l’exemple précédent) laisse une fenêtre où un signal reçu avant cette ligne ne déclenche aucun nettoyage, faute de trap encore actif. Placer le trap immédiatement après la création de chaque ressource à nettoyer, plutôt qu’une seule fois en début de script, réduit cette fenêtre au minimum.
À retenir
trap associe une commande à un signal, permettant à un script de nettoyer ses ressources avant de se terminer suite à un SIGTERM, un SIGINT, ou une sortie normale couverte par le pseudo-signal EXIT. SIGKILL et SIGSTOP restent impossibles à intercepter par nature, une limitation du kernel qui explique pourquoi un lockfile survit parfois à un kill -9 sans que ce soit un défaut du script. Placer le trap au plus près de la création de chaque ressource, plutôt qu’une seule fois en tête de script, réduit la fenêtre pendant laquelle un signal reçu trop tôt échapperait encore au nettoyage prévu.