Stop getting burned by trap signals in your long-running scripts
Technical Briefing | 9/10/2026
You write a backup script, set it to run in cron, and everything seems fine until you actually need to cancel it. Or worse, the parent process gets a SIGTERM and your script just keeps running, leaving behind stale lock files or half-written gzipped tarballs that are corrupted by design. I’ve spent too many mornings cleaning up after orphaned processes because I assumed bash would just figure out it needed to die when the lights went out.
The trap is your only line of defense
Most folks rely on exit codes, but exit codes don’t help when the shell receives a signal. If you don’t explicitly catch SIGINT or SIGTERM, your script just stops where it is, leaving the state of your system in limbo. You need to register a cleanup function that triggers regardless of how the script ends, whether it’s a clean finish or a forced termination.
cleanup() { rm -f /tmp/myapp.lock; exit; }; trap cleanup EXIT INT TERM
- The EXIT signal covers standard exits and errors
- INT handles your manual Ctrl-C intervention
- TERM is what the kernel sends when the system shuts down or a service manager pulls the plug
- Using a function name ensures your cleanup logic stays dry and maintainable
Be careful with what you put in that cleanup function. If your script crashes because of a disk write failure, don’t try to log to that same disk inside the trap. Keep the cleanup routine simple, like removing lock files or unmounting transient namespaces. If you want to log something, keep it to stderr, because stdout might already be closed or redirected elsewhere by the time your trap executes. It is not the place for complex logic, just state sanitation.
Once you make this a standard template in every script you write, you stop worrying about weird leftovers on your production boxes. The next time you see an empty PID file blocking your job, you’ll know exactly why it’s there and how to prevent it from ever showing up again.
