For Bash versions prior to "GNU bash, Version 4.2" are there any equivalent alternatives for the -v option of the test command? For example:

shopt -os nounset
test -v foobar && echo foo || echo bar
# Output: bar
test -v foobar && echo foo || echo bar
# Output: foo
  • -v isn't an option to test, but an operator for conditional expressions.
    – Tim
    May 2 '16 at 6:33
  • @Tim It is three things, beside being a token, an string and part of a line: An option to a command test -v, an operator to a conditional expression and a unary test primary for [ ]. Don't mix English language with shell definitions.
    – user79743
    May 7 '16 at 1:10

Portable to all POSIX shells:

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

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";;

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.

  • 1
    What about arrays? In Bash version "GNU bash, version 4.1.10(4)-release (i686-pc-cygwin)" echo "${foobar:+1}" doesn't print 1 if declare -a foobar was previously issued and thus foobar is an indexed array. declare -p foobar correctly reports declare -a foobar='()'. Does "${foobar:+1}" only work for non-array variables?
    – Tim Friske
    Nov 27 '12 at 9:29
  • @TimFriske ${foobar+1} (without the :, I inverted two examples in my original answer) is correct for arrays in bash if your definition of “defined” is “would $foobar work under set -o nounset”. If your definition is different, bash is a bit weird. See my updated answer. Nov 27 '12 at 10:56
  • 1
    Regarding the topic "In bash, arrays behave in a different and surprising way." the behavior can be explained from the bash(1) manpages, section "Arrays". It states that "Referencing an array variable without a subscript is equivalent to referencing the array with a subscript of 0.". Thus if neither a 0 index nor a key is defined as it is true for a=(), ${a+1} correctly returns nothing.
    – Tim Friske
    Nov 27 '12 at 21:57
  • 1
    @TimFriske I know that the bash implementation conforms to its documentation. But treating an empty array like an undefined variable is really strange design. Nov 27 '12 at 22:19
  • I prefer the result from: How to check if a variable is set in Bash? --> The Right Way ... I strikes a chord with how I thing this ought to work. Dare I say, Windows has a defined operator. Test could DO that; it can't be difficult (um ....)
    – will
    Apr 11 '16 at 14:48

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"


foobar is unset


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


foobar is set
  • Had to remove [@] if array has more than one value. Apr 17 '13 at 9:22
  • 1
    This works great, so long as you want to test whether it has a value, not whether it's been defined. I.e., foobar="" will then report that foobar is unset. No wait, I take that back. Really only tests if first element is empty or not, so it seems to be only a good idea if you know the variable is NOT an array, and you only care about emptiness, not definedness.
    – Ron Burk
    Jun 15 '15 at 2:00
  • only works if your scripts are running with undefined variables allowed (no set -u) Jan 22 '18 at 13:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.