Bash Strict Mode: When to Use set -euo pipefail
Decide whether Bash strict mode fits your production script, compare its options, and validate behavior before deploying.
03 Oct 2025, 02:58 UTC

The decision
A Bash script that continues after a command fails can deploy incomplete configuration, overwrite the wrong file, or return success while a pipeline silently dropped errors. The useful takeaway is that set -euo pipefail makes failures visible, but it is not a drop-in setting: you must explicitly handle expected failures and unset variables, or the script will abort at the wrong moment.
What each option does
Before choosing, understand the terms. An exit status is the number a command returns when it finishes: zero means success, non-zero means failure. A pipeline joins commands with |. Positional parameters are $1, $2, and $@, the arguments passed to the script.
| Option | Behavior | Best suited for | Main risk |
|---|---|---|---|
set -e | Exits immediately when a simple command returns non-zero. | Most non-interactive scripts. | Misses failures inside pipelines unless pipefail is also set. |
set -u | Treats references to unset variables as errors. | Scripts that require arguments or config values. | Fails on $@ or $1 when no arguments are passed. |
set -o pipefail | Makes a pipeline return the exit status of the first failing command, not just the last one. | Any script using |. | Not available in POSIX sh; verify Bash version first. |
set -euo pipefail | Combines all three into the common strict-mode baseline. | Production Bash scripts. | Requires explicit exception handling for expected failures. |
| No strict mode | Default Bash behavior; only the last command in a pipeline sets the exit status. | Quick interactive snippets. | Silent failures can produce corrupt or partial output. |
Trade-offs to accept
Strict mode changes control flow in ways that surprise teams migrating from informal scripts.
- Conditions ignore failure by design. A command in
if,while, oruntilmay fail without exiting the script. That is intentional: the script is testing the outcome, not requiring success. - Command substitutions and subshells can mask or propagate errors unexpectedly. A failure inside
$(...)does not always trigger an immediate exit, depending on how the result is used. - Missing arguments become fatal. With
set -u, referencing$1when no argument is supplied aborts the script before your usage check runs. ERRtraps are unreliable. A trap onERRis not guaranteed to fire for everyset -eexit, and behavior varies by Bash version.
Concrete implementation
Run these commands in a terminal where you have permission to create and edit files in the working directory. No root access is required unless the script touches protected paths.
Create deploy.sh:
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -lt 1 ]]; then
echo "usage: $0 <environment>" >&2
exit 1
fi
environment="$1"
# Explicitly check for an optional file before using it.
rollback_file="/etc/rollback/${environment}.sh"
if [[ -f "$rollback_file" ]]; then
"$rollback_file"
fi
# pipefail matters here: if the request fails, the pipeline still
# reports a non-zero exit status instead of only grep's result.
health_url="https://example.internal/health"
curl -s "$health_url" | grep "ok" > /dev/null
echo "deploy complete for $environment"
Make it executable and run it with a placeholder argument:
chmod +x ./deploy.sh
./deploy.sh staging
echo "exit status: $?"
Expect deploy complete for staging and exit status: 0 when every command succeeds. If the request fails or grep finds no match, the pipeline returns non-zero and the script stops before printing completion.
To disable strict mode locally around an expected failure, use set +e and restore it afterward:
set +e
optional_command || true
set -e
Validation steps
- Run
bash --version. Confirm Bash 4.0 or later before relying on the combined baseline. - Create a test script containing
set -euo pipefail, a failing command in a pipeline, and a reference to an unset variable. - Run
bash ./test-script.shand checkecho $?; expect a non-zero exit. - Temporarily comment out
set -u, run the script without arguments, and confirm the difference between silent empty expansion and an unbound-variable error. - Run
bash -x ./deploy.shfor a trace if the script exits earlier than expected.
Limitations
Strict mode is Bash-specific and is not POSIX sh compatible. Do not apply it to scripts with a #!/bin/sh shebang unless you confirm the interpreter is Bash. Behavior can differ between Bash versions, especially around set -e in command substitutions and ERR traps. Strict mode also does not replace input validation: it stops on failure, but it does not prevent logic errors in commands that succeed.
How to check the result
After enabling strict mode, run the script with valid input and confirm it exits zero. Then introduce one expected failure—such as a missing optional file or a failing external command—and confirm the script exits non-zero at the intended point rather than continuing silently. If it aborts too early, wrap the expected failure in if or || true, or disable strict mode locally with set +e.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.