How to test if a variable is defined at all in Bash prior to version 4.2 with the nounset shell option?

Portable to all POSIX shells:

if [ -n "${foobar+1}" ]; then
  echo "foobar is defined"
else
  echo "foobar is not defined"
fi

Make that ${foobar:+1} if you want to treat foobar the same way whether it is empty or not defined. You can also use ${foobar-} to get an empty string when foobar is undefined and the value of foobar otherwise (or put any other default value after the -).

In ksh, if foobar is declared but not defined, as in typeset -a foobar, then ${foobar+1} expands to the empty string.

Zsh doesn't have variables that are declared but not set: typeset -a foobar creates an empty array.

In bash, arrays behave in a different and surprising way. ${a+1} only expands to 1 if a is a non-empty array, e.g.

typeset -a a; echo ${a+1}    # prints nothing
e=(); echo ${e+1}            # prints nothing!
f=(''); echo ${f+1}          # prints 1

The same principle applies to associative arrays: array variables are treated as defined if they have a non-empty set of indices.

A different, bash-specific way of testing whether a variable of any type has been defined is to check whether it's listed in ${!PREFIX*}. This reports empty arrays as defined, unlike ${foobar+1}, but reports declared-but-unassigned variables (unset foobar; typeset -a foobar) as undefined.

case " ${!foobar*} " in
  *" foobar "*) echo "foobar is defined";;
  *) echo "foobar is not defined";;
esac

This is equivalent to testing the return value of typeset -p foobar or declare -p foobar, except that typeset -p foobar fails on declared-but-unassigned variables.

In bash, like in ksh, set -o nounset; typeset -a foobar; echo $foobar triggers an error in the attempt to expand the undefined variable foobar. Unlike in ksh, set -o nounset; foobar=(); echo $foobar (or echo "${foobar[@]}") also triggers an error.

Note that in all situations described here, ${foobar+1} expands to the empty string if and only if $foobar would cause an error under set -o nounset.


To sum up with Gilles' answer I made up my following rules:

  1. Use [[ -v foobar ]] for variables in Bash version >= 4.2.
  2. Use declare -p foobar &>/dev/null for array variables in Bash version < 4.2.
  3. Use (( ${foo[0]+1} )) or (( ${bar[foo]+1} )) for subscripts of indexed (-a) and keyed (-A) arrays (declare), respectively. Options 1 and 2 don't work here.

I use the same technique for all variables in bash, and it works, e.g.:

[ ${foobar} ] && echo "foobar is set" || echo "foobar is unset"

outputs:

foobar is unset

whilst

foobar=( "val" "val2" )
[ ${foobar} ] && echo "foobar is set" || echo "foobar is unset"

outputs:

foobar is set

Tags:

Bash

Test