How to write a safe Bash script
A practical checklist for writing safe Bash scripts: strict mode, quoting, mktemp, traps, input validation, and avoiding the classic footguns.
Tldr
A safe Bash script starts with `set -euo pipefail`, quotes every variable, uses mktemp for temp files, cleans up with `trap ... EXIT`, validates input, never pipes curl to bash without checksum, and avoids `rm -rf "$VAR/"` patterns where VAR could be empty.
Intro
Safe shell scripts are not about being defensive — they are about avoiding a handful of well-known footguns. This checklist covers the ones that cause real outages and security incidents.
Steps
Name
Start with strict mode
Text
`set -euo pipefail` catches unset variables, exits on error, and propagates pipeline failures. It is the closest thing Bash has to a safety net.
Name
Quote every variable
Text
Unquoted "$var" is split on whitespace and expanded as a glob. Quote every expansion unless you specifically want splitting. `rm "$file"` is safe; `rm $file` deletes multiple files when $file contains spaces or globs.
Name
Validate input
Text
Check argument count, file existence, and value ranges before using them. Bail out with a clear error rather than crashing partway.
Name
Use mktemp for temp files
Text
Never hard-code /tmp/myscript.tmp — that is a symlink-race vulnerability and breaks when two instances run concurrently. mktemp creates a file with an unguessable name in the right place.
Name
Trap cleanup on EXIT
Text
Resources that need cleanup — temp dirs, lock files, background processes — should be released by a trap on EXIT so they run on success, failure, and signal.
Name
Guard rm -rf with a non-empty check
Text
The infamous bug: `rm -rf "$BASE/"` when BASE is unset becomes `rm -rf /`. set -u helps; an explicit check is bulletproof.
Name
Verify downloaded content before executing
Text
curl | bash trusts the network, the server, and TLS. If you must do this, at least verify a checksum out of band first.
Name
Run shellcheck (and bashchecker)
Text
Static analysis catches what code review misses. ShellCheck for fast deterministic linting; Bash Checker for security review of scripts you did not write.
Faq
Q
Is "safe Bash" even possible?
A
Bash will never be as safe as a memory-safe language with a real type system. But the checklist above eliminates the bugs that cause 90% of real shell-script incidents.
Q
Should I use Python instead?
A
For anything beyond ~100 lines or any logic that needs real error handling, yes — Python has proper exceptions, typing, and standard libraries. Use Bash for what shells are good at: gluing commands together.
Q
What about sudo inside scripts?
A
Avoid it where possible. If you need root, document it and let the caller decide (`sudo ./script.sh`) rather than embedding `sudo` mid-script — that creates confusing partial-failure states.