Bash strict mode, revisited
The classic Bash “strict mode” boilerplate everyone copies:
set -euo pipefail
IFS=$'\n\t'
I’ve been using this for years and it mostly does what I want. But each of those flags has a sharp edge I keep stepping on.
set -e doesn’t propagate into && chains the way you’d
hope. cmd1 && cmd2 where cmd1 fails will not abort the
script — set -e triggers only on unchecked exit codes. If
that’s news to you, you have at least one bug.
set -u is the one I love most. ${MY_VAR:?missing} is even better
when the variable should exist: it stops the script with a message
instead of just “unbound variable: MY_VAR” with no line number.
set -o pipefail is the one I forget to enable and then debug for
30 minutes. Without it, failing_command | tee log.txt returns 0
because tee succeeded. With it, the pipeline returns the first
non-zero exit code.
Two additions I’ve adopted in 2025:
shopt -s inherit_errexit nullglob
inherit_errexit makes $(subshell) honor set -e properly —
without it, out=$(failing-cmd) does not abort. nullglob makes
for f in *.log produce an empty loop when there are no .log files,
instead of looping with literal *.log.
This is incrementally more strict every year and I’m fine with it.