What are the advantages and disadvantages of using set -u (or set -o nounset)?
Bash (like all other Bourne shell derivatives) has a feature activated by the command set -u (or set -o nounset). When this feature is in effect, any command which attempts to expand an unset variable will cause a fatal error (the shell immediately exits, unless it is interactive).
This feature is rather controversial, and has many extremely vocal advocates and opponents. It is designed to catch typos (misspelled variable names). However, it also triggers on many false positives, unless scripts are rewritten explicitly with set -u in mind. It is not always safe to add to the top of a script.
Positives
Advocates of set -u say the following:
- If you write working scripts using set -u, the code works even when set +u is used. If you write scripts using set +u you are likely to write code that does not work when using set -u. Same goes for set -e, by the way.
- Do shells generally define what is the value of an unset variable (if set +u is used)? Even if they do, you can not rely a variable is unset, even if the variable has not been set in the script; it may be set in the environment.
- Naturally unset variables expand to the empty string. Using variables without setting them to a defined value and thus potentially using environment variables works the same with or without set -u.
- Noticing misspelled variables is harder without set -u. Not all code of a script is necessarily executed on each run, so how would you catch all of your misspelled variables in a single run? Besides, continuing a script after the unbound variable error might be pointless.
If some code is not executed, set -u will have no effect on it either. You can use shellcheck to find typoed variable names. Aborting a script at an unset variable might be bad, same as continuing. The programmer needs to decide what to do either way.
Negatives
Opponents of set -u say the following:
Turning this feature on breaks many scripts that do not even contain errors. There are some extremely common shell programming idioms that rely on the expansion of positional parameters (which may not be set). For example:
while [ "$1" ]; do case $1 in -h|-?) usage;; --) shift; break;; -*) usage "Invalid option $1";; *) break;; esac done
- An empty array becomes an error (in bash 4.3, but not in bash 4.4, where there is no error even without assignment array=()):
array=() for item in "${array[@]}"; do #bash: array[@]: unbound variable : done
- A forced termination is a ridiculous way to handle a condition that isn't even always a real error. Why not simply print an error and move on, so you can catch all of your misspelled variables in a single run?
Rewriting scripts to deal with it
If you want to use set -u, you may have to rewrite portions of your script, to avoid having it trigger on false positives. For the most part, this involves identifying which parameter expansions should be "ignored", and protecting them with a construct such as this:
while [ "$1" ]; do ... # Triggers fatal error with -u while [ "${1-}" ]; do ... # Avoids fatal error with -u