Every bash script that needs a scratch directory eventually leaks one. Either ctrl-C skips the cleanup, or a set -e abort happens mid-way, or you add an early return during refactoring and forget. The fix is a trap that runs on any exit, success or failure.

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

TMP="$(mktemp -d -t myscript.XXXXXX)"
trap 'rm -rf -- "$TMP"' EXIT INT TERM HUP

echo "working in $TMP"
# ... actual work ...

EXIT alone catches normal and set -e exits. Adding INT TERM HUP catches ctrl-C, kill, and terminal hangups without re-firing EXIT twice (bash dedupes). The -- before "$TMP" is paranoia against filenames starting with a dash.

Two gotchas I have walked into:

  1. Do not use $$ to build the tmpdir name (e.g. /tmp/myscript.$$). A second run of the same script at the same PID on a container with short-lived PIDs collides.
  2. If you fork a subshell with its own trap, the parent trap still runs. Fine most of the time, but worth knowing if you start layering cleanup logic.

See also /snippets/shell-retry-with-backoff/.