Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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
foobar=
test -v foobar && echo foo || echo bar
# Output: foo
share|improve this question

3 Answers 3

up vote 10 down vote accepted

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 declared is to check whether it's listed in ${!PREFIX*}:

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

This is equivalent to testing the return value of typeset -p foobar or declare -p foobar.

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 1 if and only if $foobar would cause an error under set -o nounset.

share|improve this answer
    
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. –  Gilles Nov 27 '12 at 10:56
    
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
    
@TimFriske I know that the bash implementation conforms to its documentation. But treating an empty array like an undefined variable is really strange design. –  Gilles Nov 27 '12 at 22:19

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.
share|improve this answer

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
share|improve this answer
    
Had to remove [@] if array has more than one value. –  steinim Apr 17 '13 at 9:22

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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