15

I have an array of animals:

declare -A animals=()
animals+=([horse])

and I want to check if an animal exists or not:

if [ -z "$animals[horse]"]; then
    echo "horse exists";
fi

but this does not work.

Jahid
  • 21,542
  • 10
  • 90
  • 108
sensorario
  • 20,262
  • 30
  • 97
  • 159
  • You might have good luck with this comprehensive set of answers. https://stackoverflow.com/questions/13219634/easiest-way-to-check-for-an-index-or-a-key-in-an-array/68701581#68701581 – Anthony Rutledge Aug 08 '21 at 14:47
  • See also https://stackoverflow.com/questions/46241172/check-if-associative-array-element-exists-in-bash – U. Windl Nov 11 '22 at 08:50

4 Answers4

26

In bash 4.3, the -v operator can be applied to arrays.

declare -A animals
animals[horse]=neigh
# Fish are silent
animals[fish]=
[[ -v animals[horse] ]] && echo "horse exists"
[[ -v animals[fish] ]] && echo "fish exists"
[[ -v animals[unicorn] ]] || echo "unicorn does not exist" 

In prior versions, you would need to be more careful distinguishing between the key not existing and the key referring to any empty string.

animal_exists () {
    # If the given key maps to a non-empty string (-n), the
    # key obviously exists. Otherwise, we need to check if
    # the special expansion produces an empty string or an
    # arbitrary non-empty string.
    [[ -n ${animals[$1]} || -z ${animals[$1]-foo} ]]
}

animal_exists horse && echo "horse exists"
animal_exists fish && echo "fish exists"
animal_exists unicorn || echo "unicorn does not exist"
chepner
  • 497,756
  • 71
  • 530
  • 681
  • Typo in first section of answer above. The last line should read: `[[ -v animals[unicorn] ]] || echo "unicorn does not exist"` and not `[[ -v animals[unicorn] ]] && echo "unicorn does not exist"` Also, the test commands do not need to be double-brackets. Single brackets also work. – MrPotatoHead May 07 '21 at 09:31
  • 1
    I prefer to use the double brackets, as `-v` is not part of the POSIX standard for `[`, and if you have `-v`, you have `[[` as well. – chepner May 07 '21 at 13:20
10

There are several typos in your script

When I run it as it is, I get the following error messages from BASH:

1. animals: [horse]: must use subscript when assigning associative array
2. [: missing `]'

The first one says that if you want to use horse as an index to an associative array, you have to assign a value to it. An empty value (null) is ok.

-animals+=([horse])
+animals+=([horse]=)

The second message says that you need to separate the value you want to test and the bracket, as square bracket is considered a part of the value if not separated by spaces

-if [ -z "$animals[horse]"]; then
+if [ -z "$animals[horse]" ]; then

Finally, an element in an associative array exists when there is a value assigned to it (even if this value is null). As the question of testing if an array value is set has already been answered on this site, we can borrow the solution

-if [ -z "$animals[horse]"]; then
+if [ -n "${animals[horse]+1}" ]; then

For your convinience here is the complete script:

declare -A animals=()
animals+=([horse]=)

if [ -n "${animals[horse] + 1}" ]; then
    echo "horse exists";
fi
Community
  • 1
  • 1
Dima Chubarov
  • 16,199
  • 6
  • 40
  • 76
3

In BASH you can do:

declare -A animals=()
animals+=([horse]=)

[[ "${animals[horse]+foobar}" ]] && echo "horse exists"

"${animals[horse]+foobar}" returns foobar if horse is a valid index in array otherwise it returns nothing.

anubhava
  • 761,203
  • 64
  • 569
  • 643
0

A bit old, but came up when I was searching for an answer, my system doesnt have bash 4.3 and I was specifically concerned about animals[horse-2] example

Another way you can do it is by dumping the contents out of the the array and then checking if the substring matches -

declare -A animals
animals[horse-2]=neigh-2
animals[dog]=woof
animals_as_string=$(declare -p animals)
other_animals="horse-2 moose cow"

for animal in $other_animals
do
  if [[ "$animal" == *$animals_as_string" ]]; then
    echo "found $animal"
  fi
done

Granted you'd need something a bit more to make sure you didn't find animals[horse-2] when searching for animals[horse] (if you had that in your array)