Skip to content
AgileLogics
7 min read

Strict Mode: set -euo pipefail

Prerequisites: Error handling in scripts

By default, Bash is forgiving: it continues after errors, treats unset variables as empty, and ignores failures in the middle of pipelines. For scripts that matter, turn on strict mode at the top:

#!/usr/bin/env bash
set -euo pipefail

-e: exit on errors

The script stops as soon as a command fails:

set -e
echo "before"
ls /no/such/folder
echo "after (never printed)"
before
ls: cannot access '/no/such/folder': No such file or directory

-u: unset variables are errors

A typo in a variable name normally expands to nothing — which can be dangerous (rm -rf "$prefix/" with an empty $prefix...). -u stops instead:

set -u
backup_dir="/backups"
echo "Cleaning $backup_dri"
echo "never printed"
script.sh: line 3: backup_dri: unbound variable

Use ${var:-} when a variable may legitimately be unset.

-o pipefail: pipelines fail if any part fails

grep "x" /no/such/file | sort
echo "without pipefail: $?"

set -o pipefail
grep "x" /no/such/file | sort
echo "with pipefail: $?"
grep: /no/such/file: No such file or directory
without pipefail: 0
grep: /no/such/file: No such file or directory
with pipefail: 2

Normally a pipeline's status is its last command's (sort, which succeeded). With pipefail, a failure anywhere in the pipeline counts.

Where -e doesn't apply

set -e deliberately ignores failures in some places — commands tested by if, while, &&, and ||:

set -e
if grep -q "x" /no/such/file 2>/dev/null; then echo "found"; fi
false || echo "handled with ||"
echo "still running"
handled with ||
still running

That's what you want for intentional checks, but it also means set -e isn't a complete safety net: failures inside functions called from an if, or in command substitutions assigned with local, can slip through. Keep explicit checks (|| die "...") on critical commands.

A robust script header

# illustrative
#!/usr/bin/env bash
set -Eeuo pipefail
IFS=$'\n\t'
  • -E makes ERR traps (next lesson) work inside functions.
  • Setting IFS to newline and tab stops unquoted expansions from splitting on spaces — a safety net for quoting mistakes.

Common mistakes

  • (( count++ )) when count is 0 returns status 1 (the expression's old value was 0, which is "false") and kills the script under set -e. Use (( count += 1 )) or count=$(( count + 1 )).
  • Relying on set -e in scripts that are sourced into an interactive shell — it closes your terminal on the first error.