25

How can I test if an associative array is declared in Bash? I can test for a variable like:

[ -z $FOO ] && echo nope

but I doesn't seem to work for associative arrays:

$ unset FOO
$ declare -A FOO
$ [ -z $FOO ] && echo nope
nope
$ FOO=([1]=foo)
$ [ -z $FOO ] && echo nope
nope
$ echo ${FOO[@]}
foo

EDIT:

Thank you for your answers, both seem to work so I let the speed decide:

$ cat test1.sh
#!/bin/bash
for i in {1..100000}; do
    size=${#array[@]}
    [ "$size" -lt 1 ] && :
done
$ time bash test1.sh #best of five

real    0m1.377s
user    0m1.357s
sys     0m0.020s

and the other:

$ cat test2.sh
#!/bin/bash

for i in {1..100000}; do
    declare -p FOO >/dev/null 2>&1 && :
done
$ time bash test2.sh #again, the best of five

real    0m2.214s
user    0m1.587s
sys     0m0.617s

EDIT 2:

Let's speed compare Chepner's solution against the previous ones:

#!/bin/bash

for i in {1..100000}; do
    [[ -v FOO[@] ]] && :
done
$ time bash test3.sh #again, the best of five

real    0m0.409s
user    0m0.383s
sys     0m0.023s

Well that was fast.

Thanks again, guys.

1
  • WARNING: [[ -v FOO[@] ]] will be false if FOO is an associative array unless the key @ has been set, even if other keys have been set. declare -A FOO; FOO[bar]=bar; [[ -v FOO[@] ]] && echo true || echo false outputs "false" Commented May 2, 2024 at 1:57

4 Answers 4

19

In bash 4.3 or later, you can use the -v option:

[[ -v FOO[@] ]] && echo "FOO set"

Note that in any version, using

declare -A FOO

doesn't actually create an associative array immediately; it just sets an attribute on the name FOO which allows you to assign to the name as an associative array. The array itself doesn't exist until the first assignment.

Sign up to request clarification or add additional context in comments.

14 Comments

I am so far unable to find any case in which "FOO[@]" in the above code can produce any different outcome than just "FOO". If the intent was to also detect that FOO is not an array, I don't know of any solution that doesn't require using declare (the array size check offered below doesn't help).
I guess a new enough bash could avoid declare by using ${FOO@A} to get the same information about FOO.
It doesn't work. $ declare -A FOO; [[ -v FOO[@] ]] && echo "FOO set" never runs "echo". bash 4.4.x
@Kirby Because FOO isn't set; all you've done is added an array attribute to the name FOO. It doesn't become set (which is what -v tests) until you actually assign a value to the array.
This doesn't determine whether FOO is an associative array -- it would return the same way for a numerically-indexed one as well.
|
16

You can use declare -p to check if a variable has been declared:

declare -p FOO >/dev/null 2>&1 && echo "exists" || echo "nope"

And to check specifically associative array:

[[ "$(declare -p FOO 2>/dev/null)" == "declare -A"* ]] &&
   echo "array exists" || echo "nope"

8 Comments

You can't use declare -p to check if a variable has been declared if the variable has not been set.
@Jani not sure what you're stating. The declare will return a non-zero exit status (contrary to what the help command claims) if the variable has not been set. @anubhava appears to have a typo in the second example (should be 2>/dev/null I think). Otherwise, this seems to be the only functioning answer if you want to detect whether the name was declared as an associative array.
This solution is working. Thank you! (By the way, "selected" answer doesn't...)
@Jani just in case, unset a; declare -A a; declare -p a &> '/dev/null' && echo 'exists';.
@Faither That behaviour was added/fixed in bash 4.4 in 2016. The relevant bash changelog entry says, "The '-p' option to declare and similar builtins will display attributes for named variables even when those variables have not been assigned values (which are technically unset)." :)
|
8

This is a Community Wiki version of an excellent answer by @user15483624 on a question which is now closed as duplicate. Should that user choose to add their own answer here, this should be deleted in favor of the one with their name on it.


The prior answers on this question should be used only when compatibility with bash 4.x and prior is required. With bash 5.0 and later, an expansion to check variable type is directly available; its use is far more efficient than parsing the output of declare -p, and it avoids some of the unintended side effects of other proposals as well..

The following can be used to test whether a bash variable is an associative array.

[[ ${x@a} =~ A ]]

${x@a} can be used to test whether it is a variable and an array as well.

$ declare x; echo "${x@a}"

$ declare -a y; echo "${y@a}"
a
$ declare -A z; echo "${z@a}"
A
$ declare -A -x a; echo "${a@a}"
Ax

7 Comments

Note that ${x@a} returns A even if x is not set. However this allows for building a safe test that some key exists: [[ ${x@a} = A && -v x[key] ]] && echo "x[key] is set" (works also under set -u).
@fuujuhi What Bash version are you referring to? Without declaring x or with explicitly unsetting it: unset x; echo "${x@a}" I get empty string, so I can not confirm your statement. bash --version returns 5.0.17(1)-release here.
@Cromax, if I recall the context in which it was made correctly, the statement from fuujuhi was in reference to the declared-but-unset case, not the both-undeclared-and-unset case. (Also, that's "unset" as in "never assigned a value", not as in "had a value but it was removed with unset")
@CharlesDuffy You're right, my bad. I guess I was too hasty. Anyway it seems impossible (except for declare -p method) to check if regular (non-array) variable has been declared AND not set.
Do note that ${z@a} expands to a list of attributes that may also contain other attributes, like r for readonly, x for export, etc.
|
3

One of the easiest ways is to the check the size of the array:

size=${#array[@]}
[ "$size" -lt 1 ] && echo "array is empty or undeclared"

You can easily test this on the command line:

$ declare -A ar=( [key1]=val1 [key2]=val2 ); echo "szar: ${#ar[@]}"
szar: 2

This method allow you to test whether the array is declared and empty or undeclared altogether. Both the empty array and undeclared array will return 0 size.

Note: If using set -e or set -u, make sure you understand the consequences of using set -e and set -u.

6 Comments

People who use set -e should be aware of the consequences. People who use set -u shoud be aware of the advantages and disadvantages of using set -u.
IMHO, when our scripts have set -eu then we could add | true like this size=${#array[@]} | true to prevent execution interruptions but our scripts will be "outputtingh" with bash: array: unbound variable
This test does not distinguish between ar being unset versus being declared an associative array with no entries. It is critical to know the difference, because the interpretation of ar[subscript] is different in the 2 cases. When ar is unset, subscript is interpreted as a bash arithmetic expression, where as when ar is an empty associative array, it is treated as a string without further processing.
@OldPro - that is quite astute and quite true. However, the statement still holds "Both the empty array and undeclared array will return 0 size". You can test the empty case both declare -a ar and/or declare -A ar, and the undeclared case (without any declare). In all cases, size is returned as 0. (confirmed on GNU bash, version 5.2.26(1)-release)
David, I am not disagreeing that the statement "Both the empty array and undeclared array will return 0 size" holds true. Rather, I am agreeing that it is true and pointing out that, because the OP and I need a test to determine if the array has been declared or not, and this test does not definitively answer that question, this is not an answer to the question.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.