The Bash trap command lets a script catch signals and other events and run a command when they happen. You use it to clean up temporary files, print a message, or run a final command when a script exits, is interrupted, or receives a specific signal. This article explains the syntax, common uses, and pitfalls of trap, with examples you can adapt.
What Does trap Do?
In Bash, trap registers a handler—a command or function—that runs when the shell receives a specific signal or event. The most common use is trap 'rm -f "$tmpfile"' EXIT, which removes a temporary file no matter how the script ends (normal exit, error, or Ctrl+C). Without trap, an interrupted script can leave behind lock files, temp files, or a terminal in the wrong state.
The command works for both signals (like INT for Ctrl+C, TERM for kill) and pseudo-signals (like EXIT, ERR, DEBUG). The syntax is:
trap 'command' SIGNAL
For example, trap 'echo "Caught Ctrl+C"; exit 1' INT runs the echo and exits when the user presses Ctrl+C. You can also use trap - SIGNAL to reset a signal to its default behavior, and trap -p to list current traps.
Why Use trap? The Problem It Solves
Shell scripts often create temporary files, change directory, or set terminal modes. If the script is killed or fails midway, those changes persist. For instance, a script that does cd /tmp and then is interrupted leaves the user’s shell in /tmp? Actually, that only affects the script’s own subshell, not the parent. But a script that writes to a temp file and then crashes leaves the file behind. trap ensures cleanup runs even on abnormal termination.
Another common problem: a script that runs a long process, and the user presses Ctrl+C. By default, the script dies immediately, possibly leaving a child process running or a lock file. With trap 'cleanup' INT, you can kill the child, remove the lock, and exit gracefully.
Consider a script that sets the terminal to raw mode (for a game or password prompt). If it crashes, the terminal stays broken. trap 'stty sane' EXIT restores it.
How to Use trap: Syntax and Signals
The basic form is trap [action] [signal...]. The action can be a string (a command) or a function name. If the action is an empty string (trap '' INT), the signal is ignored. If you omit the action and just give a signal, it resets to the default.
Common signals you’ll trap:
EXIT– runs when the shell exits, regardless of how (normal end,exitcommand, or receiving a signal that would terminate it).INT– sent when the user presses Ctrl+C.TERM– sent by thekillcommand by default.HUP– sent when the terminal closes (hangup).ERR– runs after a command returns non-zero (ifset -eis not active, it still runs).DEBUG– runs before every simple command.RETURN– runs when a function or sourced script returns.
You can list multiple signals: trap 'cleanup' INT TERM EXIT. Note that trapping EXIT is enough for most cleanup because it fires on normal exit and on termination by a signal (unless the shell is killed with SIGKILL, which cannot be trapped).
Common Pitfalls and Misconceptions
One big mistake: using trap 'rm -f "$tmpfile"' EXIT but defining $tmpfile after the trap line. The trap runs the command at exit time, so it uses the current value of the variable. If the variable is empty, it will try to remove an empty path. Always define the variable before setting the trap, or use a function that checks.
Another pitfall: trapping EXIT and then calling exit inside the trap. That causes an infinite loop because the trap runs again. Use exit only if you want to override the exit status, and be careful. To avoid recursion, unset the trap first: trap - EXIT; exit 1.
A common misconception is that trap can catch SIGKILL or SIGSTOP. It cannot—these are uncatchable by design. Also, trap does not catch errors by default; you need ERR or set -e.
Another mistake: forgetting that trap runs in the shell’s context, not a subshell. If you use trap 'cd /somewhere' EXIT, the trap runs in the script’s shell, so it can change the working directory for any subsequent commands in the trap, but not for the parent shell.
trap vs. set -e and Other Cleanup Methods
Bash offers several ways to handle errors and cleanup, and they serve different purposes:
set -e(errexit) makes the script exit immediately on any command returning non-zero. It does not run cleanup unless you also use a trap.set -u(nounset) treats unset variables as errors.trap ... ERRruns a command when any command fails, but does not exit by itself.trap ... EXITis the standard way to ensure cleanup runs at the end of the script.
Many scripts combine set -e with trap 'cleanup' EXIT. That works well: if a command fails, the script exits, and the EXIT trap runs cleanup. However, set -e has quirks—it does not trigger on commands in if conditions, in && or || lists, or in functions unless called in a context that would exit. So a failed command inside an if won’t cause an exit, but the ERR trap will still run.
Another alternative is to use a finally-like pattern with a function and trap, but Bash does not have a native finally block. The trap command is the closest equivalent.
When Not to Use trap
Sometimes you don’t need a trap. If your script is short and does not create resources, a trap is overkill. Also, if you want to ignore a signal entirely (e.g., prevent Ctrl+C from stopping a script), you can use trap '' INT, but be careful: the script will not respond to Ctrl+C, and the user may have to kill it with kill -9.
For simple scripts that just run a few commands and exit, adding a trap can introduce complexity. For example, if you trap EXIT and then your trap command itself fails, it may mask the original exit status. To preserve the exit status, you can capture it: status=$?; trap 'exit $status' EXIT—but that is only needed if the trap command changes the status.
Also, avoid using trap to catch ERR for every single command in a long script unless you really need line-by-line logging; it can slow down the script and produce noisy output.
Frequently Asked Questions
Q: What is the difference between trap ... EXIT and trap ... 0?
A: They are the same. Signal 0 is the EXIT pseudo-signal. Both run the command when the shell exits. Using EXIT is more readable.
Q: Can I trap multiple signals with one trap command?
A: Yes, list them separated by spaces: trap 'cmd' INT TERM EXIT. The command runs when any of those signals are received.
Q: How do I reset a trap to default?
A: Use trap - SIGNAL. For example, trap - INT restores the default Ctrl+C behavior. You can also use trap -p to see current traps.
Q: Does trap work in functions?
A: Yes, but with a caveat. If you set a trap inside a function, it applies to the shell that runs the function (the script’s shell), not just the function. When the function returns, the trap remains unless you reset it. Use trap - SIGNAL at the end of the function to restore.
Q: Why doesn’t my trap run when I press Ctrl+C?
A: If you are running the script in the background or with nohup, Ctrl+C may not send SIGINT. Also, if the script is waiting for a child process, the child may receive the signal first and the shell may not. Try trapping INT and TERM together, and ensure the script is in the foreground.
Q: Can I use trap to catch errors in a pipeline?
A: By default, ERR trap does not trigger on a pipeline’s non-zero exit unless the last command fails. With set -o pipefail, the pipeline returns non-zero if any command fails, and then the ERR trap runs. Without pipefail, only the last command’s status matters.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.
More cards in this drawer
- Channel TLD Websites: What They Are & How to Get One5.00 min/km
- Django Class Based Views CRUD REST API
- Most Secure OS for Privacy: 3 Picks by Device
- Ryobi One+ Battery System: Is the Switch Worth It?
- Tmux Terminal Upgrade: Is It Worth It?
- Refurbished Tech Buying Checklist