100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Bash

trap and Signal Handling

Master bash's `trap` builtin to intercept signals like SIGINT and EXIT, enabling reliable cleanup, graceful shutdown, and robust error handling in scripts.

Process & Job ControlAdvanced10 min readJul 10, 2026
Analogies

What trap Does

The trap builtin registers a command or function to run when the shell receives a specified signal, letting scripts intercept events like Ctrl-C (SIGINT), termination requests (SIGTERM), or even bash's own pseudo-signals such as EXIT (fires on any script exit, normal or otherwise) and ERR (fires whenever a command returns non-zero, useful alongside set -e). This turns a script from something that can leave temp files, locks, or background processes behind into one that cleans up deterministically no matter how it terminates.

🏏

Cricket analogy: Just as a captain has a rehearsed response the moment rain starts falling mid-over — covers on, players off — trap lets a script have a rehearsed response the instant a signal like SIGINT arrives, rather than being caught off guard.

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

TMPDIR=$(mktemp -d)
LOCKFILE=/tmp/myscript.lock

cleanup() {
  local exit_code=$?
  echo "Cleaning up (exit code $exit_code)..." >&2
  rm -rf "$TMPDIR"
  rm -f "$LOCKFILE"
  exit "$exit_code"
}

# Fires on normal exit, error exit, or receipt of INT/TERM
trap cleanup EXIT
trap 'echo "Interrupted by user"; exit 130' INT
trap 'echo "Terminated"; exit 143' TERM

touch "$LOCKFILE"
echo "Working in $TMPDIR ..."
sleep 30

Common Signals and the EXIT/ERR Pseudo-Signals

SIGINT (2) is what Ctrl-C sends and is typically caught to allow a graceful abort message; SIGTERM (15) is the default signal sent by kill and orchestrators like systemd or Kubernetes during shutdown, and should be handled to flush buffers or release resources within the grace period before SIGKILL (9) arrives, which cannot be trapped at all because it bypasses the process's signal handling entirely. Bash's EXIT pseudo-signal fires on any script termination path — whether it falls off the end, calls exit, or dies from an untrapped signal — making it the single best place to put idempotent cleanup logic instead of duplicating it across every possible exit point.

🏏

Cricket analogy: SIGKILL is like a match being abandoned outright by the ground authority with zero notice to either team, whereas SIGTERM is a rain delay warning that gives the umpires and players a few minutes to cover the pitch and get to safety.

In containerized environments, PID 1 does not get default signal handlers unless it explicitly traps them, and an unhandled SIGTERM sent to a bash script running as PID 1 in a container is ignored entirely, causing docker stop to hang until the 10-second grace period expires and Kubernetes/Docker escalates to SIGKILL — always trap SIGTERM explicitly in container entrypoints.

trap with ERR and DEBUG

trap 'handler' ERR runs whenever a command returns a non-zero exit status, similar to set -e but observable, and is commonly used to log the failing line number via $LINENO and $BASH_COMMAND before the script exits — note ERR does not fire inside conditions of if, while, until, or commands connected with &&/||, matching set -e semantics. trap 'handler' DEBUG is a more aggressive tool that fires before every simple command, useful for tracing execution or implementing custom timeouts, but it adds meaningful overhead and is normally reserved for debugging sessions rather than production scripts.

🏏

Cricket analogy: An ERR trap is like the third umpire automatically reviewing every dismissal appeal only when it's genuinely contested, while a DEBUG trap is like reviewing literally every single ball bowled in the match, technically possible but far more overhead than needed.

Trap handlers registered with single quotes, like trap 'echo $var' EXIT, defer expansion of $var until the trap fires, capturing its value at signal time; using double quotes instead expands $var immediately when trap is called, which is usually not what you want for variables that change during the script.

  • trap 'command' SIGNAL registers a handler; trap 'command' EXIT fires on any script termination path.
  • SIGKILL (9) can never be trapped or ignored; SIGTERM (15) is the polite request that should be trapped for graceful shutdown.
  • PID 1 in a container does not get default signal dispositions — always trap SIGTERM explicitly in entrypoint scripts.
  • trap ... ERR mirrors set -e semantics: it skips commands inside conditionals and short-circuit &&/|| chains.
  • trap ... DEBUG fires before every simple command and is powerful for tracing but too expensive for production use.
  • Use single quotes around trap commands so variables are expanded when the signal fires, not when trap is registered.
  • Combine $?, $LINENO, and $BASH_COMMAND inside trap handlers to log exactly what failed and where.

Practice what you learned

Was this page helpful?

Topics covered

#Bash#AdvancedBashScriptingStudyNotes#DevOps#TrapAndSignalHandling#Trap#Signal#Handling#Does#StudyNotes#SkillVeris