TIL: set -u in bash
Trying a new Today I learned section… Today, I learned about set -u
,
which will fail a bash script if we try to use a variable that's not defined.
No more deleting someone's files because a variable was not defined. Demo:
#!/bin/bash set -u GREETING=Hello #oups I misspelt that echo ${GREETINGS} world
If you run this:
$ ./script ./script: line 7: GREETINGS: unbound variable
That makes testing whether a variable is set slightly more complicated:
set -u if [[ -z ${GREETINGS} ]]; then # oups we never get here, the previous line fails with "unbound variable" GREETINGS=$( ....compute greetings ...) fi
Instead:
set -u if [[ ! -v GREETINGS ]]; then GREETINGS=$( ... compute greetings ... ) fi
Where -v
is a Bash Conditional Expression described thusly:
- -v varname
- True if the shell variable varname is set (has been assigned a value).
I suppose the actually equivalent test would be:
\[\[ ! -v GREETINGS ]] || \[\[ -z ${GREETINGS} ]]
,
to check whether the variable is not only set but also non-null.
I've always used set -e
and set -o pipefail
in shell scripts. The first
will exit the entire script if any command (more specifically: any pipeline
fails). However by default, quoting the manual: the "exit status of a pipeline
is the exit status of the last command in the pipeline", meaning the following
succeeds:
$ false | true $ echo $? 0
But, if "pipefail
is enabled, the pipeline's return status is the value of
the last (rightmost) command to exit with a non-zero status, or zero if all
commands exit successfully", so:
$ set -o pipefail $ false | true $ echo $? 1
TL;DR: Use set -euo pipefail
at the top of your bash scripts. Julia Evans
agrees.