DevOps

Bash Script Auto-Exit on Error with set -e Guide

Learn how to make Bash scripts automatically exit with code 1 on any command failure using set -e and strict mode set -euo pipefail. Avoid manual $? checks for reliable error handling in bash scripts, pipelines, and DevOps automation.

9 answers 1 view

How to make a Bash shell script automatically exit with code 1 if any command returns a non-zero exit status, without explicitly checking each command?

I have a Bash script that runs multiple commands. I want it to abort immediately and exit with return value 1 upon any command failure, avoiding manual checks like this:

bash
dosomething1
if [[ $? -ne 0 ]]; then
 exit 1
fi

dosomething2
if [[ $? -ne 0 ]]; then
 exit 1
fi

What is the best way to achieve this behavior in Bash?

Use set -e right after your shebang in the Bash script—it forces an immediate exit with the failing command’s exit code (usually 1 for errors) whenever any simple command fails, skipping those tedious $? checks entirely. For even tighter control, go with the full strict mode: set -euo pipefail, which also bombs out on unset variables and pipeline glitches. This approach keeps your Bash scripts lean, reliable, and fail-fast, just like you’d expect from modern scripting.


Contents


Introduction to Error Handling in Bash Scripts

Ever had a Bash script chug along after a command tanks, only to spew garbage or worse, pretend everything’s fine? That’s the default behavior—Bash ignores non-zero exit codes unless you babysit with if [[ $? -ne 0 ]]; then exit 1; fi after every line. Annoying, right? But flipping that script on its head is simple and built-in.

The goal here: make your script abort instantly on any failure, exiting with code 1 (or whatever the bad command spat out). No more manual checks cluttering your code. This is core Bash error handling, and it’s a game-changer for automation, DevOps pipelines, or any Linux workflow where reliability matters.


Using set -e for Automatic Exit

Here’s the trick everyone overlooks at first: set -e, also known as errexit. Slap it at the top of your script, and poof—any command returning non-zero triggers an instant exit with that exact code.

bash
#!/bin/bash
set -e

# These will run fine
echo "Starting up"
ls /tmp

# This fails? Script exits here with ls's code (2 for no such file/dir)
ls /nonexistent
echo "This never prints"

Run it: bash myscript.sh; echo $? spits out 2. Clean. You can even invoke via bash -e myscript.sh without editing the file.

Why does this work? The Bash manual spells it out: pipelines, lists, or simple commands exit the shell on non-zero status. But it’s not perfect—more on gotchas later. For now, ditch the boilerplate checks; set -e handles 80% of cases.


Strict Mode: set -euo pipefail

set -e alone? Solid starter. But pair it with friends for bulletproof Bash scripts: set -euo pipefail. Breaks down like this:

  • -e: Exit on any non-zero command (errexit).
  • -u: Treat unset variables as errors—no more silent echo $foo bombs.
  • -o pipefail: Pipelines fail if any stage fails, not just the last one.

Full shebang:

bash
#!/bin/bash
set -euo pipefail

Test a pipeline: false | true without pipefail returns 0 (success!). With it? Exits 1. Perfect for grep | awk chains in real scripts.

As SS64 docs note, this combo is the gold standard—no extra cost, just safer code. Pro tip: Add -x for debug traces if you’re troubleshooting (set -euxo pipefail).


Common Pitfalls and Exceptions

But wait—set -e isn’t magic. It skips errors in certain spots, per the Bash FAQ. Here’s what trips folks up:

  1. Conditionals and loops: if false; then echo nope; fi or while false; do ...; done—the test itself doesn’t trigger exit.
bash
set -e
if ls /nonexistent; then echo "Won't happen"; fi
echo "But this prints anyway"
  1. Pipelines (pre-pipefail): cmd1 | cmd2 ignores early fails.

  2. Arithmetic: ((i=0; i++)) or let i++ can quietly fail.

  3. Assignments that look like them: local var=$(badcmd) sweeps errors under the rug.

  4. &&/|| lists: Only the final chain matters.

Workarounds? Use || true to ignore deliberately, or strict mode covers most. And remember: subshells inherit it separately.

Frustrating at first, but knowing these keeps scripts predictable.


Alternatives: Traps and Custom Functions

Hate the exceptions? Roll your own with traps or functions. Traps shine for logging before exit.

From Intoli’s guide:

bash
set -e
trap 'last_command=$current_command; current_command=$BASH_COMMAND' DEBUG
trap 'echo "\"${last_command}\" command failed with exit code $?."' EXIT

Now failures print: "ls /fake" command failed with exit code 2. Elegant.

Or a custom checker:

bash
exit_on_error() {
 exit_code=$?
 last_command="${@:2}"
 if [ $exit_code -ne 0 ]; then
 echo "\"${last_command}\" failed with ${exit_code}." >&2
 exit $exit_code
 fi
}
# Usage: ls /fake; exit_on_error $? !!

Trap ERR for inheritance: set -E; trap 'echo Error at line $LINENO' ERR. Flexible when set -e feels too blunt.


Practical Examples

Real-world? Say you’re deploying:

bash
#!/bin/bash
set -euo pipefail

cd /app || { echo "Can't cd to /app"; exit 1; }
git pull
npm install
npm test
systemctl restart myapp

Fails at git pull? Done, exit 1. No partial deploys.

CLI test:

bash
$ bash -e -o pipefail -u <<EOF
echo "Test"
false | true
echo "Nope"
EOF

Exits early.

For loops with checks:

bash
set -e
for dir in /tmp/*; do
 [ -d "$dir" ] || continue # Skip non-dirs without exiting
 ls "$dir"
done

These snippets scale from quick hacks to production pipelines.


Best Practices

Fail fast, always. Debian packaging mandates set -e for a reason—unhandled errors cascade badly.

  • Start every script with set -euo pipefail.
  • Log via traps: trap 'echo "Failed at $BASH_COMMAND" >&2' ERR.
  • Test edge cases: pipes, arith, conditionals.
  • Debug: Temporarily add -x.
  • Explicit ignores: cmd || true.
  • Exit codes: Use specifics (127 for not found) over generic 1.

Avoid over-relying on set -e in complex logic—mix with explicit checks. As this Stack Overflow thread debates, traps offer nuance without pitfalls.

Your scripts? Bulletproof now.


Sources

  1. Automatic exit from bash shell script on error — Comprehensive Q&A on set -e, strict mode, and gotchas: https://stackoverflow.com/questions/2870992/automatic-exit-from-bash-shell-script-on-error
  2. Exit on errors in Bash scripts — Practical traps for error logging and custom exit handlers: https://intoli.com/blog/exit-on-errors-in-bash-scripts/
  3. The Set Builtin (Bash Manual) — Official documentation on set -e, pipefail, and shell options: https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html
  4. What does “set -e” mean in a Bash script? — Discussion of errexit behavior, exceptions, and best practices: https://stackoverflow.com/questions/19622198/what-does-set-e-mean-in-a-bash-script
  5. set command (Bash) — Quick reference for set options including errexit and pipefail: https://ss64.com/bash/set.html

Conclusion

Stick set -euo pipefail in your Bash scripts, layer on traps for polish, and wave goodbye to manual error babysitting—your code exits cleanly with the right code on any hiccup. Pitfalls exist, but awareness fixes them; the payoff is scripts that just work, every time. Next deployment? You’ll thank yourself when it fails loudly before shipping broken bits.

A

Put set -e at the top of the script. This will cause the shell to exit immediately if a simple command exits with a nonzero exit value.

S

Code set -e will immediately terminate your script with an exit status of 1 if any command exits with a non-zero status.

M

Use Bash’s errexit option. Add set -e (or set -o errexit) at the top of your script. This causes the shell to exit immediately when any command returns a non-zero status.

S

Try trap 'exit 1' ERR. This will cause the script to exit with code 1 on any error.

E

The simplest way is set -e. It causes Bash to exit immediately upon encountering any command failure. Enhance with set -u and set -o pipefail for comprehensive error handling.

G

Use set -e at the start of your script to exit immediately on command failure. Combine with set -o pipefail for pipes: #!/usr/bin/env bash set -euo pipefail.

Use set -e or set -o errexit to exit immediately if a pipeline (which may consist of a single simple command), a list, or a compound command exits with a non-zero status.

SS64.com / Documentation

Use set -e to exit immediately if a simple command exits with a non-zero status. Use set -o pipefail so pipe failures are caught.

Authors
A
S
Developer
S
Developer
E
Blog Author
G
Sources
Stack Overflow / Q&A platform
Q&A platform
Intoli / Blog
Blog
Documentation
SS64.com / Documentation
Documentation
Verified by moderation
Moderation
Bash Script Auto-Exit on Error with set -e Guide