GICT knowledge base
How to Use the trap Command in Bash
Published
Ever written a Bash script that’s supposed to run for a while say, copying files, processing data, or maybe spinning through a long loop and found yourself wondering what happens if you hit Ctrl+C or if the system randomly kills your script? If you haven’t thought about it yet, trust me: you will eventually! The first time you find your /tmp directory clogged with leftover temp files, or realize your script left a lockfile behind and now nothing else can run, you’ll start wishing there was a way to clean up after yourself, no matter how your script ends.
That’s where the trap command comes in. It’s one of those tools that feels almost invisible until you need it. Once you get the hang of it, you’ll wonder how you ever wrote scripts without it. So let’s break it down, step by step, in plain language, with plenty of examples and a few stories from the trenches.
What is trap and Why Should You Care?
In Bash (and most POSIX shells), the trap command lets you catch signals things like someone hitting Ctrl+C, or the script finishing, or even an unexpected error and then do something before your script exits. The “something” can be as simple as cleaning up temporary files, removing a lock file, or just printing a friendly goodbye message.
Signals are kind of like the ways your computer and your scripts communicate “urgent news.” For example, when you hit Ctrl+C, the shell sends a SIGINT (interrupt) to your script. If another process calls kill on your script, it might send SIGTERM. The trap command gives you a chance to intercept those signals, do some housekeeping, and then exit gracefully.
Why use it?
Let’s be honest: most scripts, especially the quick-and-dirty ones, don’t bother with trap at all. But if your script is doing anything with temporary files, lockfiles, network connections, background processes, or really anything that might leave a mess if it’s interrupted, trap is your best friend.
Getting Started: The Basics of trap
Here’s the simplest version:
trap 'echo "Goodbye!"' EXIT
With that line in your script, “Goodbye!” will print out every time your script exits no matter if it finished normally, crashed, or was interrupted.
Let’s see a more practical example:
#!/bin/bash
TEMPFILE=$(mktemp)
trap 'rm -f "$TEMPFILE"; echo "Cleaned up."' EXIT
echo "Working with $TEMPFILE..."
sleep 10
echo "Done!"
Try running that, then hit Ctrl+C during the sleep. You’ll see “Cleaned up.” And if you check /tmp, your temp file will be gone. Magic!
Catching Specific Signals
While EXIT is a sort of “catch-all,” you can also trap individual signals. The most common are:
- SIGINT (2): Sent when you hit Ctrl+C.
- SIGTERM (15): The default signal when you use the kill command.
- SIGHUP (1): Sent when a terminal closes or a process is disconnected.
Here’s how you might handle just Ctrl+C:
trap 'echo "You pressed Ctrl+C! Exiting..."; exit 130' SIGINT
while true; do
echo "Working... (press Ctrl+C to quit)"
sleep 2
done
When you press Ctrl+C, you’ll get a custom message and the script will exit with exit code 130 (which is a convention for an interrupted process).
Real-World Story: The Forgotten Lockfile
I’ll never forget the time I wrote a deployment script that used a lockfile to make sure only one copy ran at a time. It worked until someone killed the script with Ctrl+C. The lockfile was still there, so the next run refused to start, even though nothing was actually running. We scratched our heads for a while until someone noticed the stale lock.
After that, I always used:
trap 'rm -f /tmp/my.lock' EXIT
No more leftover locks.
Advanced Usage: Custom Cleanup Functions
You don’t have to put all your cleanup code inline. In fact, it’s often neater to write a function:
cleanup() {
echo "Doing cleanup..."
rm -f "$TEMPFILE"
# Any other cleanup steps here
}
trap cleanup EXIT
If your script is long or complicated, this makes it much easier to manage. Plus, you can call cleanup manually if you need to.
Multiple Signals, One Trap
You can list several signals in one go:
trap 'cleanup' SIGINT SIGTERM SIGHUP
Now the same cleanup code runs if you hit Ctrl+C, or if the process is killed, or if the terminal closes.
Ignoring Signals
Sometimes, you might want your script to ignore interrupts entirely. Maybe you don’t want the user to be able to kill it with Ctrl+C. (Use this with care! It can be frustrating if overused.)
trap '' SIGINT
Now, Ctrl+C does nothing. To restore default behavior:
trap - SIGINT
Unsetting a Trap
If you need to remove a trap during your script (maybe after your critical section is done), just use:
trap - EXIT
After that, the EXIT trap is no longer active.
Example: Cleaning Up in a Temporary Directory
Suppose your script makes a temp directory for its work:
WORKDIR=$(mktemp -d)
trap 'rm -rf "$WORKDIR"' EXIT
cd "$WORKDIR"
# Do stuff here
If the script is interrupted at any point, the temp directory is deleted. No more disk clutter!
Trap and Error Handling
You can combine trap with error handling for robust scripts. For example, you might want to clean up if anything fails, not just on normal exit:
set -e # Exit on any error
trap 'cleanup' EXIT
cleanup() {
local code=$?
echo "Script exited with code $code"
rm -rf "$WORKDIR"
}
Now, no matter how the script ends, cleanup happens and you know the exit code too.
Pitfalls and Tips
- Quote your trap code: Always use single quotes around your trap commands. Otherwise, Bash might expand variables too early.
trap 'rm -f "$TEMPFILE"' EXIT - Set the trap early: Place your trap commands as close to the top as possible, before you create files or start critical actions.
- Traps in subshells: Traps don’t automatically propagate into subshells. If you run code in ( … ), set the trap inside the subshell if needed.
- Test your traps: Try interrupting your script in different ways (Ctrl+C, kill, normal exit) to make sure your trap works as you expect.
FAQ
Can I trap multiple signals at once?
Yes. Just list them: trap 'cleanup' SIGINT SIGTERM
Will trap work if the system is powered off?
No. If the system crashes or loses power, the script can’t run cleanup code.
What signals can I trap?
Most, but not all. You can’t trap SIGKILL (9) or SIGSTOP (19).
Can I see which traps are active?
Run trap -p to list current traps.
Conclusion
The trap command is one of those little Bash tricks that transforms your scripts from fragile one-offs into professional, reliable tools. With a simple line or two, you can handle interruptions gracefully, clean up after yourself, and avoid the classic problems that plague so many shell scripts.
Whether you’re automating a build, processing files, or writing internal tools, think about what could go wrong and use trap to make sure your scripts behave well, even when life (or the user) throws a wrench in the works.
If you haven’t tried using trap yet, give it a spin in your next script. You’ll quickly see why it’s a must-have for anyone serious about shell scripting.
Related guides
How to Set Up zram on a Small VPS
Configure zram on Ubuntu with kernel and swap checks. Understand compressed memory limits, monitor real use and avoid unsafe swap-file changes.
LinuxHow to Find Services That Need a Restart After Updates with needrestart
Find services using outdated libraries with needrestart on Ubuntu. Read restart reports, review automatic restart policy and plan maintenance safely.
LinuxHow to Limit a Linux Service’s Memory with systemd
Limit a Linux service with systemd MemoryHigh, MemoryMax and swap controls. Check cgroup support, diagnose pressure and remove only your own policy.
LinuxHow to Protect SSH with CrowdSec on Ubuntu
Protect SSH with CrowdSec on Ubuntu. Connect log detection to firewall enforcement and test temporary blocking without losing administrator access.
