33

Please note: there are many questions about how to test a single shell variable on this site. This question is about testing a script for any undefined variable.

You can use an undefined variable in bash without seeing any error at execution:

#!/bin/bash

echo ${UNDEF_FILE}
ls -l ${UNDEF_FILE}

exit 0

I've found this very error prone. If I want to change the name of a variable in a large script, or remove that variable, all the previous stale references will cause errors in the script. Sometimes this is not obvious to debug or you find out when it's too late.

Why is this allowed? Is there any way to flag undefined variables?

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
Robert Kubrick
  • 8,413
  • 13
  • 59
  • 91
  • The "why"s go back to compatibility with 1970s-era shells. If you care about best-practices, as opposed to history, static checking -- as with http://shellcheck.net/, which is also available for download [as a standalone tool](https://github.com/koalaman/shellcheck) -- is your friend here. – Charles Duffy Dec 07 '16 at 15:18
  • 1
    Frankly, making undefined variables' expansion erroneous will break many common idioms. `[[ $var ]] & { echo "Doing something with $var"; }`, for instance, will break with `set -u`: Unlike `set -e` (which makes only failures which aren't checked or used as conditionals into failures), `set -u` makes *every* reference to an undefined variable an error. – Charles Duffy Dec 07 '16 at 15:20
  • 1
    (By the way -- all-caps variable names are in space defined by POSIX for environment variables with meaning to the shell or operating system; the namespace of variables containing at least one lower-case character is reserved for application use. This applies to all shell variables, as opposed to environment variables only, on account of setting a shell variable sharing a name with an environment variable overwriting the latter). – Charles Duffy Dec 07 '16 at 15:22
  • BTW, was that edit ("Please note: there are many questions about how to test a single shell variable on this site. This question is about testing a script for any undefined variable.") pointed at me? If so, I suggest that you may have misunderstood my prior comments. The point I was making is that `[[ $var ]]` **DOES NOT WORK** if `set -u` is in use, not that it's an available mechanism to check whether a single variable is unset. Likewise for `[ -n "$var" ]`, `[ -z "$var" ]`, etc; you need a different set of idioms if using `set -u`. – Charles Duffy Dec 07 '16 at 15:29

2 Answers2

45

You can use:

set -u

at the start of your script to throw an error when using undefined variables.

-u

Treat unset variables and parameters other than the special parameters "@" and "*" as an error when performing parameter expansion. If expansion is attempted on an unset variable or parameter, the shell prints an error message, and, if not interactive, exits with a non-zero status.

Community
  • 1
  • 1
anubhava
  • 761,203
  • 64
  • 569
  • 643
  • 6
    ...however, it's worth pointing out that the utility of this is... controversial: Many common idioms depend on undefined values expanding to a null string, so code needs to be explicitly written for `set -u` compatibility if one wants it to be so. – Charles Duffy Dec 07 '16 at 15:18
  • 5
    `set -o nounset` has the same effect as `set -u` (in Bash), and some people prefer it. – pjh Dec 07 '16 at 16:56
  • 2
    See [Test if a variable is set in bash when using “set -o nounset”](http://stackoverflow.com/q/7832080/4154375) for ways to handle common problems caused by using `set -u` or `set -o nounset`. – pjh Dec 07 '16 at 17:01
  • @pjh I was trying to find more info on how to use undefined variables in script with `set -u` or `set -o nounset` is set and this answer is really helpful. – anubhava Dec 07 '16 at 17:07
3

set -u is the more general option, but as pointed out in other answers' comments, there are problems writing idiomatic shell scripts with set -u in play. An alternative is to create parameter expansions that yield an error when a specific variable isn't set.

$ echo $foo

$ echo $?
0
$ echo "${foo?no foo for yoo}"
bash: foo: no foo for yoo
$ echo $?
1

This error will cause a non-interactive shell to exit. This gives you a quick way to guarantee an error condition won't allow control flow to continue with an undefined value. The spec does not require an interactive shell to exit, although it's worth noting that even in an interactive shell, bash will return from a function call if this error occurs in a function.

PoolloverNathan
  • 148
  • 2
  • 11
kojiro
  • 74,557
  • 19
  • 143
  • 201