← retour aux snippets

bash: trap cleanup et signaux

Nettoyer et terminer proprement avec trap: cleanup, ERR, EXIT et signaux INT/TERM.


objectif

Utiliser trap pour garantir un cleanup fiable (tmp, verrous), remonter les erreurs (ERR) et réagir aux signaux (INT, TERM, HUP) sans laisser d’états sales.

code minimal

#!/usr/bin/env bash
set -Eeuo pipefail

# ressources
tmp="$(mktemp)"; lock="/tmp/myjob.lock"
cleanup() {
  rm -f "$tmp" "$lock" 2>/dev/null || true
  # tuer les jobs en arrière-plan du script
  jobs -pr | xargs -r kill -TERM 2>/dev/null || true
}
on_err() {
  local rc=$?; echo "error rc=$rc at line ${BASH_LINENO[0]}: ${BASH_COMMAND}" >&2
}

# traps
trap on_err ERR          # nécessite set -E pour propager dans les fonctions
trap cleanup EXIT INT TERM HUP

# section critique (exclusivité simple)
exec 9>"$lock"; flock -n 9 || { echo "already running"; exit 1; }

# travail: si interrompu, cleanup s'exécute
echo "running... tmp=$tmp"
sleep 3
echo "done"

utilisation

# 1) trap spécifique pour réagir au SIGINT/SIGTERM (arrêt coopératif)
graceful_stop() { echo "stopping..."; }
trap graceful_stop INT TERM

# 2) protéger un pipeline: remonter l'échec d'une étape (pipefail)
set -o pipefail
curl -fsSL https://api.data.pm/health | jq -e .status >/dev/null   # si jq échoue, ERR se déclenche

# 3) timeout + cleanup d'un worker en arrière-plan
start_worker() { while :; do date -u +%H:%M:%S; sleep 1; done; }
start_worker & wid=$!
( sleep 5; kill -TERM "$wid" 2>/dev/null || true ) &
wait "$wid" || true  # EXIT trap s'occupera de ce qu'il reste

# 4) désactiver un trap temporairement (section sensible)
trap - ERR
# ... opérations qui ne doivent pas déclencher on_err ...
trap on_err ERR

# 5) inspecter les traps actifs (debug)
trap -p

variante(s) utile(s)

# tracer les commandes échouées uniquement (xtrace sélectif)
on_err() {
  local rc=$? line=${BASH_LINENO[0]} cmd=${BASH_COMMAND}
  printf 'ERR rc=%d line=%s cmd=%q\n' "$rc" "$line" "$cmd" >&2
}
set -Eeuo pipefail; trap on_err ERR

# cleanup avancé: temp dir + fermeture de FD
tmpdir="$(mktemp -d)"
cleanup() {
  exec 9>&-  # ferme le FD 9 si ouvert
  rm -rf "$tmpdir" 2>/dev/null || true
}
trap cleanup EXIT INT TERM HUP

# relayer le signal au process enfant principal (supervision simple)
run_and_forward() {
  "$@" & child=$!
  forward() { kill -TERM "$child" 2>/dev/null || true; }
  trap forward INT TERM
  wait "$child"
}
run_and_forward "node" "server.js"

# traps dans des fonctions: assurer la propagation avec `set -E`
set -E
f() { false; }  # provoque ERR
trap 'echo "ERR in function at line ${BASH_LINENO[0]}" >&2' ERR
f || true

notes

  • trap ... EXIT s’exécute toujours en fin de script (même en cas d’erreur) → idéal pour le cleanup.
  • trap ... ERR nécessite set -E pour se propager dans les fonctions et sous-shells déclenchés par command substitution.
  • utilisez pipefail pour ne pas masquer les échecs dans les pipelines.
  • capturez et tuez les jobs en arrière-plan (jobs -pr | xargs kill) pour éviter les orphelins.
  • réagissez à INT/TERM pour un arrêt gracieux; laissez EXIT assurer le nettoyage final.