Two tips:
Fail fast
You probably already know that you can force Bash scripts to exit immediately if there’s an error (that is, if any command exits with a non-zero exit code) using:
#!/usr/bin/env bash
set -e
but it’s even better to use:
set -eu -o pipefail
so that the script:
- exits on an error (
-e
, equivalent to-o errexit
); - exits on an undefined variable (
-u
, equivalent to-o nounset
); - exits on an error in piped-together commands (
-o pipefail
)1.
You can learn more about these built-ins with:
help set
Fail noisy
Failing fast is all very well but, by default scripts fail silently offering no debug information.
You can add:
set -x
to print each command before execution but it’s easy to add simple error
handling using trap
:.
function print_error {
read line file <<<$(caller)
echo "An error occurred in line $line of file $file:" >&2
sed "${line}q;d" "$file" >&2
}
trap print_error ERR
Here we bind the print_error
function to the ERR
event and print out an
error message and offending line of the script (extracted using sed
) to
STDERR.
Note the use of a <<<
here string and the caller
built-in to assign the
line and filename of the error.
So running the script:
#!/usr/bin/env bash
set -eu -o pipefail
function print_error {
read line file <<<$(caller)
echo "An error occurred in line $line of file $file:" >&2
sed "${line}q;d" "$file" >&2
}
trap print_error ERR
false
gives:
$ ./script.sh
An error occurred in line 11 of file ./test.sh:
false
There are more sophisticated ways to handle errors in Bash scripts2 but this is a concise, simple option to have to hand.
-
Without this
pg_dump ... | gzip
would not be treated as an error even ifpg_dump
exited with a non-zero exit code. ↩︎ -
See the Bash infinity framework for example. ↩︎