A variable in bash (and any POSIX-compatible shell) can be in one of three states:
- unset
- set to the empty string
- set to a non-empty string
Most of the time you only need to know if a variable is set to a non-empty string, but occasionally it's important to distinguish between unset and set to the empty string.
The following are examples of how you can test the various possibilities, and it works in bash or any POSIX-compatible shell:
if [ -z "${VAR}" ]; then
echo "VAR is unset or set to the empty string"
fi
if [ -z "${VAR+set}" ]; then
echo "VAR is unset"
fi
if [ -z "${VAR-unset}" ]; then
echo "VAR is set to the empty string"
fi
if [ -n "${VAR}" ]; then
echo "VAR is set to a non-empty string"
fi
if [ -n "${VAR+set}" ]; then
echo "VAR is set, possibly to the empty string"
fi
if [ -n "${VAR-unset}" ]; then
echo "VAR is either unset or set to a non-empty string"
fi
Here is the same thing but in handy table form:
+-------+-------+-----------+
VAR is: | unset | empty | non-empty |
+-----------------------+-------+-------+-----------+
| [ -z "${VAR}" ] | true | true | false |
| [ -z "${VAR+set}" ] | true | false | false |
| [ -z "${VAR-unset}" ] | false | true | false |
| [ -n "${VAR}" ] | false | false | true |
| [ -n "${VAR+set}" ] | false | true | true |
| [ -n "${VAR-unset}" ] | true | false | true |
+-----------------------+-------+-------+-----------+
The ${VAR+foo}
construct expands to the empty string if VAR
is unset or to foo
if VAR
is set to anything (including the empty string).
The ${VAR-foo}
construct expands to the value of VAR
if set (including set to the empty string) and foo
if unset. This is useful for providing user-overridable defaults (e.g., ${COLOR-red}
says to use red
unless the variable COLOR
has been set to something).
The reason why [ x"${VAR}" = x ]
is often recommended for testing whether a variable is either unset or set to the empty string is because some implementations of the [
command (also known as test
) are buggy. If VAR
is set to something like -n
, then some implementations will do the wrong thing when given [ "${VAR}" = "" ]
because the first argument to [
is erroneously interpreted as the -n
operator, not a string.