Scripts that create temp files, start services, or change state have a duty: clean up on every exit path — success, error, Ctrl+C, or kill. bash's trap builtin is how you keep that promise, and one line covers almost every case.
The one-line version #
#!/usr/bin/env bash
set -euo pipefail
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
# ... work with $tmpdir, safe on every exit path ...
trap 'commands' SIGNAL registers commands to run when that signal arrives. EXIT fires when the script ends for any reason — normal exit, an error under strict mode, Ctrl+C, kill. One line, total coverage: that is why this pattern is the answer nine times out of ten.
Signals you will actually trap #
| Signal | Arrives when | Typical use |
|---|---|---|
EXIT (0) | Script ends, any way | Cleanup — the default |
INT (2) | Ctrl+C | Graceful stop + cleanup |
TERM (15) | kill default | Same, for service-like scripts |
HUP (1) | Terminal closed | Same family |
EXIT covers INT and TERM implicitly — when the script dies from a signal, the EXIT trap still runs. You only trap INT/TERM explicitly to add behaviour beyond cleanup, like printing "interrupted" or shutting down a child process first:
srv_pid=$(start_server)
trap 'kill "$srv_pid" 2>/dev/null' EXIT
The details that matter #
- Single quotes in the trap body delay expansion:
'rm -rf "$tmpdir"'reads$tmpdirat trap-fire time, when it holds the right value. Double quotes expand immediately — sometimes what you want, usually not. - Traps run before the script finishes exiting, so variables are still in scope.
trap - EXITremoves a trap — useful for scoped sections: set it, do the risky work, clear it.- A trap can change the exit code by exiting itself — leave it alone unless you mean to.
The rollback shape #
acquire_lock
trap 'release_lock; echo "aborted" >&2' EXIT
if ! upload; then
exit 1 # trap releases the lock
fi
trap - EXIT # success: keep the lock deliberately
That set-trap/clear-trap bracket is the general pattern: provisional state, made permanent only after every step survived.
Temp files via mktemp + EXIT trap is the minimal safe recipe; the signals vocabulary behind INT and TERM lives in the process notes. The scripting series continues.
