100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Bash

Bash Strict Mode

An explanation of the `set -euo pipefail` pattern known as bash strict mode, what each flag actually does, and where its edge cases can still surprise you.

Production BashIntermediate8 min readJul 10, 2026
Analogies

The set -euo pipefail Pattern

"Strict mode" is the community name for starting a script with set -euo pipefail, three flags combined to make bash behave closer to how most programmers instinctively expect a scripting language to behave: stop on error, stop on undefined variables, and don't hide errors inside pipelines. -e (errexit) exits the script the moment any command returns non-zero, -u (nounset) turns any reference to an unset variable into a fatal error instead of a silent empty string, and -o pipefail makes a pipeline's exit status reflect the first failing stage instead of only the last command. None of these are bash's default behavior, which is why scripts written without them can run for years hiding bugs that only strict mode would have caught immediately.

🏏

Cricket analogy: Strict mode is like playing under DRS with every decision reviewable, instead of the old system where an umpire's single incorrect call stood uncorrected for the rest of the innings.

How errexit (-e) Actually Behaves

-e has notoriously inconsistent edge cases that trip up even experienced scripters: it does not trigger inside a condition tested by if, while, or until; it does not trigger for any command that is part of a && or || chain except the last one; and it does not trigger for commands whose exit status is captured, like x=$(false), unless that assignment is itself checked. This means grep pattern file || true deliberately suppresses errexit for that one command (a common idiom for 'this command is allowed to fail'), while some_function failing inside an if some_function; then is expected and does not stop the script, since that's precisely what the condition is testing.

🏏

Cricket analogy: It's like a no-ball rule that only voids the delivery when the bowler oversteps on their front foot, but not when a fielder's boundary-rope contact happens during a completely separate phase of play — the exception is scoped precisely.

bash
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'

# errexit does NOT fire here: failure is the condition being tested
if grep -q 'ERROR' app.log; then
  echo "errors found"
fi

# Explicitly allow a command to fail without killing the script
rm -f /tmp/stale.lock || true

# nounset catches this typo immediately instead of silently using an empty string
# echo "$USRE_HOME"   # would exit: USRE_HOME: unbound variable

# pipefail ensures a failing first stage is not masked by a successful grep
curl -sf https://api.example.com/status | grep -q '"ok":true'

nounset, pipefail, and IFS Hardening

-u (nounset) is the cheapest bug-catcher in strict mode: a mistyped variable name like $USRE_HOME instead of $USER_HOME normally expands to an empty string and silently corrupts downstream logic, but under -u bash exits immediately with 'unbound variable'. The one caveat is that arrays and positional parameters need care — $@ and $* are safe, but referencing $1 when no arguments were passed will trigger nounset, so scripts often guard with ${1:-} when an argument is genuinely optional. Many strict-mode scripts also set IFS=$'\n\t' to remove the space from bash's default word-splitting characters, which prevents accidental splitting of filenames containing spaces during unquoted loops, though the real fix is still to quote expansions properly rather than rely on IFS alone.

🏏

Cricket analogy: nounset catching a typo'd variable is like a strict scorer who refuses to log a run for a batter not officially in the lineup, immediately flagging the discrepancy instead of quietly crediting the wrong player.

When an optional positional parameter is legitimate, guard it explicitly: verbose="${1:-false}" avoids nounset failures while still documenting the default clearly, rather than disabling -u for the whole script.

Strict mode is not a silver bullet: -e is silently ignored inside command substitutions used in certain contexts, and functions called as the last command in an &&/|| chain do not trigger errexit even on failure. Always pair strict mode with explicit checks on commands whose failure truly matters, rather than assuming three flags make the script bulletproof.

  • Strict mode is set -euo pipefail: errexit on failure, nounset on undefined variables, pipefail on masked pipeline errors.
  • -e does not trigger inside if/while/until conditions or for non-final commands in && / || chains — these are intentional exemptions.
  • -u turns typo'd or undefined variable references into immediate fatal errors instead of silent empty-string corruption.
  • Guard genuinely optional parameters with ${1:-default} rather than disabling nounset for the whole script.
  • -o pipefail makes a pipeline's exit status reflect the first failing stage, not just the final command.
  • Setting IFS=$'\n\t' reduces accidental word-splitting on spaces but is not a substitute for quoting expansions.
  • Strict mode reduces an entire class of silent bugs but is not exhaustive — explicit checks are still needed for commands whose failure matters most.

Practice what you learned

Was this page helpful?

Topics covered

#Bash#AdvancedBashScriptingStudyNotes#DevOps#BashStrictMode#Strict#Mode#Set#Euo#StudyNotes#SkillVeris