Bash strict mode: writing ops scripts that fail loudly
set -euo pipefail is the start, not the whole story. Traps, quoting, and safe temp files for scripts that run as root.
A Bash script that plows on after an error is how a backup job 'succeeds' every night while shipping an empty tarball. By default Bash ignores failures, treats unset variables as empty strings, and reports a pipeline as successful if the last command worked. Three flags flip all of that so your scripts fail loudly instead of failing silently.
The difference is not subtle — a typo'd cd that keeps going can run a rm -rf in the wrong directory:
bash deploy.sh # no strict modedeploy.sh: line 4: cd: /srv/relese: No such file or directoryrm -rf * then ran in $HOME — the typo did not stop the script bash deploy.sh # with set -euo pipefaildeploy.sh: line 4: cd: /srv/relese: No such file or directoryaborted at line 4 — nothing destructive ranThe header, line by line
Put this at the top of every script. Setting IFS to just newline and tab stops the other classic footgun — word-splitting on spaces when you loop over output.
#!/usr/bin/env bashset -euo pipefailIFS=$'\n\t'# -e exit the moment any command fails# -u unset variable is an error, not ""# -o pipefail a failed stage fails the whole pipeline
Clean up with traps
-e means your script can exit at any line, so cleanup can't live at the bottom — it'll get skipped. A trap on EXIT runs on success, on error, and on Ctrl-C alike. Pair it with mktemp so you never hardcode a temp path two scripts can collide on.
tmp="$(mktemp -d)"trap 'rm -rf "$tmp"' EXIT # always runs, however we leavecurl -fsSL "$URL" -o "$tmp/archive.tgz"tar -xzf "$tmp/archive.tgz" -C "$tmp"
Quote everything
An unquoted variable is split on whitespace and glob-expanded before the command ever sees it. Quote every expansion, and use arrays — not space-joined strings — whenever you build an argument list.
# wrong — word-splits and globs on the contents of $pathrm -rf $path/*# right — quote scalars, use an array for arg listsrm -rf "$path"/*args=(--config "app prod.conf" --force)mycmd "${args[@]}"
Where this goes next
Make the machine enforce it: run shellcheck in CI and it'll flag the unquoted variables, the useless cats, and the [ ] tests that should be [[ ]] before they reach main. Strict mode catches failures at runtime; ShellCheck catches them at review.