One line belongs at the top of nearly every bash script:

Strict mode, the whole line
set -euo pipefail

It is not magic — it is three independent settings, each changing one way bash silently tolerates failure. Scripts with this line fail early, loudly, and legibly; scripts without it usually fail late and quietly. Here is what each letter buys.

-e: stop on failure #

Without -e, bash runs every line no matter what the last one did — a failed copy followed by a delete of the "successful" backup is a genre of outage. With set -e, any command that fails ends the script, unless the failure is checked:

Checked failures do not trigger -e
set -e
cp app.conf out/          # failure here stops the script
if grep -q debug app.conf; then   # checked: fine either way
  echo "debug on"
fi
cmd || true               # explicitly allowed to fail

The if/&&/|| exceptions matter: anything you deliberately test is exempt. See exit codes for the grammar.

-u: no unset variables #

The typo that -u catches
set -u
config="${confi_path}"    # typo: bash exits, "unbound variable"

Without -u, an unset variable expands to empty and the script continues with whatever that emptiness means — often rm -rf "$dir/" with an empty $dir. With it, typos in variable names become immediate, named errors. Parameter expansion defaults (${1:-}) remain the way to say "empty is fine".

-o pipefail: the whole pipe must work #

By default, a pipeline's status is its last command's — so curl -sf url | grep x succeeds even when curl failed, and grep happily reports no matches on empty input. pipefail makes the pipeline fail if any stage fails:

Why pipefail matters
set -euo pipefail
curl -sf "$url" | jq -r '.key'    # a failed download now stops the script

Combined with -e, a broken upstream stops everything instead of laundering garbage downstream.

The fine print #

SettingCatches
-eUnchecked command failures
-uUnset and misspelled variables
-o pipefailFailures hidden inside pipelines

-E andare not unconditional: grep that "fails" by finding nothing, arithmetic that evaluates to zero, and commands in if conditions all behave differently under -e. The debugging note shows how to trace these with bash -x.

Guards for the failure paths belong to traps; readable failure messages come from exit codes. The scripting series continues.