How to test if a variable is defined at all in Bash prior to version 4.2 with the nounset shell option?
Solution 1
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
.
Solution 2
To sum up with Gilles' answer I made up my following rules:
- Use
[[ -v foobar ]]
for variables in Bash version >= 4.2. - Use
declare -p foobar &>/dev/null
for array variables in Bash version < 4.2. - 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.
Solution 3
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
Related videos on Youtube
Tim Friske
Updated on September 18, 2022Comments
-
Tim Friske almost 2 years
For Bash versions prior to "GNU bash, Version 4.2" are there any equivalent alternatives for the
-v
option of thetest
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
-
Tim about 8 years
-v
isn't an option totest
, but an operator for conditional expressions. -
Admin about 8 years@Tim It is three things, beside being a token, an string and part of a line: An
option
to a commandtest -v
, anoperator
to aconditional expression
and aunary test primary
for[ ]
. Don't mix English language with shell definitions.
-
-
Tim Friske over 11 yearsWhat about arrays? In Bash version "GNU bash, version 4.1.10(4)-release (i686-pc-cygwin)"
echo "${foobar:+1}"
doesn't print1
ifdeclare -a foobar
was previously issued and thusfoobar
is an indexed array.declare -p foobar
correctly reportsdeclare -a foobar='()'
. Does"${foobar:+1}"
only work for non-array variables? -
Gilles 'SO- stop being evil' over 11 years@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 underset -o nounset
”. If your definition is different, bash is a bit weird. See my updated answer. -
Tim Friske over 11 yearsRegarding 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 fora=()
,${a+1}
correctly returns nothing. -
Gilles 'SO- stop being evil' over 11 years@TimFriske I know that the bash implementation conforms to its documentation. But treating an empty array like an undefined variable is really strange design.
-
rj487 about 11 yearsHad to remove [@] if array has more than one value.
-
Ron Burk about 9 yearsThis 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 thatfoobar 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. -
will about 8 yearsI 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 ....) -
Gilles 'SO- stop being evil' about 8 years@will My first command is the same as the “Right Way”, we just inverted the test (if set vs if unset). The rest of my answer is a digression on bash arrays. The
test
command doesn't do that because it was historically implemented as a separate command (and it still is, but most if not all modern and even not-so-modern shells also have it built in). -
Florian Heigl over 6 yearsonly works if your scripts are running with undefined variables allowed (no set -u)