The defensive header
#!/usr/bin/env bash
set -u
set -o pipefailShell options are design choices, not a professionalism badge. -u and pipefail can expose mistakes; -e has context-dependent exceptions and must be tested against the script's control flow. Add only the options whose behavior the script understands.
What each flag does
- -e (errexit) — exit immediately when any command returns non-zero (with carve-outs for tests and pipelines).
- -u (nounset) — exit when an undefined variable is referenced. Catches typos like
$PROOJECT. - -o pipefail — pipeline exit code is the first non-zero stage's, not just the last command's.
- IFS — controls splitting in specific expansion and read contexts. Quote expansions and set IFS locally where a data format requires it instead of changing it globally by ritual.
Carve-outs you'll need
set -e doesn't fire if the command is part of an if, a && chain, or piped into |. Use that to your advantage:
# OK — caller checks the exit code
if grep -q ERROR build.log; then ...
# Optional cleanup with a narrow, validated target
cache_dir="${project_root:?}/.cache"
if [[ -d "$cache_dir" ]]; then
rm -r -- "$cache_dir"
fiDefault with -u
With -u, referencing an unset variable explodes. The ${var:-} form gives an empty default, ${var:-fallback} gives a real one. Always defensive-default user-supplied env vars: "${DEBUG:-0}".
Custom error reporting
err() { echo "$0:${LINENO}: $*" >&2; exit 1; }
[[ -d "$dir" ]] || err "missing $dir"A small err helper makes script failures actionable. Pair with trap (next lesson) for cleanup on the way out.
errexit is not exception handling
Commands used as tests, parts of AND/OR lists, and some pipeline or subshell contexts interact with set -e differently. Check expected failures explicitly and test each important branch instead of assuming every nonzero status aborts.
Inspect which pipeline stage failed
pipefail changes the aggregate status but does not explain the broken stage. Capture diagnostics or split critical pipelines so recovery can name the producer or consumer that failed.