Reputation: 335
Since bash
4.2, the -v
conditional expression has been added. It's used to check if a variable is set (has been assigned a value). It's a useful tool when writing scripts that run under set -o nounset
, because attempting to use a non-initialized variable causes an error.
I have one issue with it, see the sample in a POSIX bash
(set -o posix
):
$ declare testvar=0
$ [ -v testvar ] && echo "It works!"
It works!
$ declare -A fizz=( [buzz]=jazz )
$ [ -v fizz[buzz] ] && echo "It works!"
It works!
$ [ -v fizz ] && echo "It doesn't work :("
$
As you can see, using -v
on a regular variable works as expected. Using it to check the presence of a field within an associative array works as expected as well. However, when checking the presence of the associative array itself, it doesn't work.
Why is that, and is there a workaround?
Upvotes: 8
Views: 273
Reputation: 70822
check4Associative() {
local foo typ
read foo typ foo < <(declare -p $1 2>&1)
[ "$typ" ] && [ -z "${typ//-*A*}" ]
}
You could use as:
if check4Associative myVar; then ... ;fi
Sample tries:
unset fizz
check4Associative fizz && echo yes;echo $?
1
fizz=Blah
check4Associative fizz && echo yes;echo $?
1
declare -A fizz
check4Associative fizz && echo yes;echo $?
yes
0
fizz[foo]=bar
check4Associative fizz && echo yes;echo $?
yes
0
check4AssociativeArgs() {
local -n var=$1
local keys=(${!var[@]}) foo typ
read foo typ foo < <(declare -p $1 2>&1)
[ "$typ" ] && [ -z "${typ//-*A*}" ] &&
printf "Name: %s, type: %s, entry count: %d\n" $1 ${typ#-} ${#keys[@]}
}
Then
unset fizz
check4AssociativeArgs fizz ; echo $?
1
declare -A fizz
check4AssociativeArgs fizz
Name: fizz, type: A, entry count: 0
fizz[foo]=bar fizz[bar]=baz
check4AssociativeArgs fizz
Name: fizz, type: A, entry count: 2
Upvotes: 1
Reputation: 15293
If you want to know if it has any entries, you can check ${#fizz[@]}
, but that doesn't tell you if it has been created empty. You can use a case statement or other pattern matching in conjunction with error checking.
tst() { local tst;
(( $# )) || return # *minimal* error handling...
if tst="$(declare -p $1 2>&1)" # does the subshell error?
then case "$tst" in # if not, check the output
*\)*) (( $( eval echo \${#$1[@]} ) )) && # test number of entries
echo "Has Args" || # some implementations show '()'
echo "Empty but defined";; # some don't; this is for the *do*
*-A*) echo "Empty but defined";; # this is for the don't
*) echo ERROR;; # shouldn't get here
esac
else echo Undefined # if doesn't exist
fi
}
$: unset fizz
$: tst fizz
Undefined
$: declare -A fizz
$: tst fizz
Empty but defined
$: fizz[buzz]=jazz
$: tst fizz
Has Args
Dunno how useful that will be in your context though. Note that I wrote this one explicitly for associative arrays with minimal error checking not directly related to the point. For any sort of real production use this should be expanded a lot.
I don't like the eval
, but my bash version doesn't have namerefs. :o/
Upvotes: 1