Thuta Learning
AdvancedDevOps & Toolsbeginner

Signal Handling with trap

What you'll walk away with

  • Explain the core ideas behind Signal Handling with trap
  • Run the sample code and verify its output
  • Apply the technique correctly to the Tutorial Platform and production scenarios

Build the mental model

trap 'command' SIGNAL registers a piece of code — a single command or, more usefully, a function name — to run whenever bash receives that signal, and it stays registered until you replace it or the shell exits. The most useful signal by far is EXIT, a pseudo-signal (not a real POSIX signal) that bash itself fires whenever the script finishes for any reason at all: normal completion falling off the last line, an early exit call, or a set -e abort partway through. Inside an EXIT handler, $? still holds the exit status that triggered it, which is useful if you want cleanup to behave slightly differently after a failure versus a clean run. trap 'rm -f "$tmpfile"' EXIT guarantees a temp file gets deleted no matter which of those paths the script takes, which is far more reliable than putting a single rm at the bottom of the script and hoping every code path — including every early exit and every error — actually reaches it. You can also trap real signals like SIGINT (sent when someone presses Ctrl+C) and SIGTERM (sent by kill by default) to let a script shut down gracefully instead of dying wherever it happened to be — finishing a write, releasing a lock, printing a partial-progress message — before exiting. One thing worth knowing up front: trap does not stack handlers. Calling trap a second time for the same signal doesn't add a second handler alongside the first; it silently replaces it, so if a script needs several cleanup actions on EXIT, they all belong inside one function registered once, not several separate trap calls.

Connect it to a real scenario

Imagine a backup script that downloads a large file to a temp path, verifies its checksum, then compresses it and uploads the result — several steps, each of which could be interrupted. If the user hits Ctrl+C halfway through the download, or the network drops and the script errors out under set -e, an untrapped script just stops wherever it was, leaving a half-downloaded temp file sitting on disk with no cleanup step ever reached. Adding trap cleanup EXIT right after the temp file is created — where cleanup is a function that does rm -f "$tmpfile" — changes that: this handler now runs automatically once, whichever of the script's possible endings actually happens, normal completion, a set -e triggered error, or an interrupt caught by a separate SIGINT/SIGTERM trap that calls exit. Because the EXIT trap fires last regardless of the path taken, you only have to write the cleanup logic once, in one place, instead of duplicating a rm call before every exit point and every error branch in the script — which is exactly the kind of thing that's easy to add today and easy to forget three edits from now.

Try the working example

bash
#!/bin/bash
set -euo pipefail

tmpfile=$(mktemp)
echo "created temp file: exists=$([ -f "$tmpfile" ] && echo yes)"

cleanup() {
  rm -f "$tmpfile"
  echo "cleanup ran: tmpfile removed"
}
trap cleanup EXIT

on_interrupt() {
  echo "caught SIGINT, shutting down gracefully"
  exit 1
}
trap on_interrupt SIGINT SIGTERM

echo "doing some work..."
echo "some data" > "$tmpfile"
echo "wrote to tmpfile, contents: $(cat "$tmpfile")"

echo "script ending normally, EXIT trap will fire next"
You should see
Running the script above prints:

created temp file: exists=yes
doing some work...
wrote to tmpfile, contents: some data
script ending normally, EXIT trap will fire next
cleanup ran: tmpfile removed

5-minute try-it

Write a script that creates two temp files and registers a single EXIT trap that removes both. Run the script normally, then run it again and press Ctrl+C midway through, checking each time that no temp files are left behind.

One important caution

Assuming multiple trap ... EXIT calls stack up — each call replaces the previous handler for that signal, so only the last trap you registered runs; combine all cleanup into one handler instead

Setting the trap before the resource it cleans up actually exists (e.g. trapping on $tmpfile before mktemp has run) — if something fails in between, the trap fires with an empty or wrong path and cleans up nothing

GNU Bash Manual — SignalsBash / Shell Scripting

Easy traps

  • Assuming multiple trap ... EXIT calls stack up — each call replaces the previous handler for that signal, so only the last trap you registered runs; combine all cleanup into one handler instead
  • Setting the trap before the resource it cleans up actually exists (e.g. trapping on $tmpfile before mktemp has run) — if something fails in between, the trap fires with an empty or wrong path and cleans up nothing
  • Validate sample code in a local or test environment before applying it to a production system.

Exercise

Write a script that creates two temp files and registers a single EXIT trap that removes both. Run the script normally, then run it again and press Ctrl+C midway through, checking each time that no temp files are left behind.

You'll know it worked when: Running the script above prints: created temp file: exists=yes doing some work... wrote to tmpfile, contents: some data script ending normally, EXIT trap will fire next cleanup ran: tmpfile removed

Signal Handling with trap | Thuta Learning