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.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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
    
-v isn't an option to test, but an operator for conditional expressions. – Tim yesterday
up vote 21 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 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.

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
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. – Gilles 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 at 14:48

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

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

This simple test (with double brackets) in BASH:

[[ ${foobar[@]} ]] && echo "foo (set)" || echo "bar (unset or null)"

works always. Lets make it a function, and test all possible values:

$ testvar(){  [[ ${foobar[@]} ]] && echo "foo (set)" || echo "bar (unset or null)"; }

$ unset foobar; testvar
bar (unset or null)

$ unset foobar; foobar=''; testvar
bar (unset or null)

$ unset foobar; foobar='baz'; testvar
foo (set)

$ unset foobar; foobar[2]=''; testvar
bar (unset or null)

$ unset foobar; foobar[2]='baz'; testvar
foo (set)

$ unset foobar; foobar[2]='baz'; foobar[3]='zef'; testvar
foo (set)

For POSIX shells, it is enough to test:

[ "${foobar+1}" ] && echo "foo (set)" || echo "bar (unset or null)"

However, that will not work for array values in shells that actually accept arrays.

In zsh: the test works correctly, even for arrays, except when some array index is set to null: foobar[2]='' reports (incorrectly) foobar as set.

share|improve this answer

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.