Error Handling in Scripts
Prerequisites: Exit codes, &&, and ||, Functions
By default, Bash keeps going after a command fails. In a script, that
can turn one error into a disaster — cd fails, and the next rm runs in
the wrong folder. Robust scripts check for failure.
Check important commands
die() { echo "Error: $*" >&2; exit 1; }
mkdir -p output
cd output || die "cannot enter output"
echo "data" > result.txt || die "cannot write result.txt"
echo "Saved $(pwd | sed 's#.*/##')/result.txt"
cd /no/such/folder || die "cannot enter /no/such/folder"
echo "never printed"
Saved output/result.txt
script.sh: line 7: cd: /no/such/folder: No such file or directory
Error: cannot enter /no/such/folder
command || die "..." is short and readable. Note that the failing cd
printed its own error message too.
Checking in if statements
if ! grep -q "ERROR" <<< "all good"; then
echo "No errors found"
fi
if output=$(ls /no/such/folder 2>&1); then
echo "Listing: $output"
else
echo "ls failed with: $output"
fi
No errors found
ls failed with: ls: cannot access '/no/such/folder': No such file or directory
if output=$(command) captures the output and checks the status in
one step.
set -e: stop on the first error
set -e makes the script exit as soon as a command fails:
set -e
echo "Step 1"
false
echo "Step 2 — never reached"
Step 1
It's a useful safety net — Level 3's strict mode combines it with
other options — but it has surprising exceptions (for example, failures
inside if conditions and && chains don't trigger it), so explicit
checks on important commands are still good practice.
Meaningful exit codes
Exit 0 on success and a non-zero code on failure. Some scripts use
different codes for different problems (for example, 1 general
error, 2 bad usage), and document them in the usage message.
Common mistakes
- Assuming a pipeline fails when an early command fails. By default, a
pipeline's status is that of its last command:
false | truesucceeds.set -o pipefail(Level 3) changes that. - Printing errors to stdout. Send them to stderr with
>&2, so they aren't mixed into output that another program is reading.