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.