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.
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.
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"
[[ -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.-v is not part of the POSIX standard for [, and if you have -v, you have [[ as well.[[ -v foo[bar] ]] works when your script has set -u. Otherwise you get an error if you try to expand a nonexistent array member, so things like test -n "${foo[bar]}" will not work.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
animals[horse]= than to use +=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.
= at the end of line "animals+=([horse]=)"horse. animals+=([horse]) will give syntax error otherwise.Use ! in your array reference to get the keys. Use * to get all the keys as a space-separated string. My testing shows you could use @ instead of * even though they work slightly differently in some cases. The curly braces around test_key are not required. I use them out of habit. The parenthesies around ${test_key} may be replaced with double-quotes. Single quotes expand differently and will cause the test to fail.
declare -A animals=(["horse"]="equine" ["cat"]="feline" ["cow"]="tasty") # ...etc
test_key="fish"
if [[ ${!animals[*]} =~ (${test_key}) ]]; then
# ^ ^
# do stuff if key does exist
else
# do stuff if key doesn't exist
fi
This also works in-line:
[[ ${!animals[*]} =~ (${test_key}) ]] && do stuff if true || do stuff if false
The only place I can think this might have trouble is if you search for a partial key and there are multiple keys with the same partial pattern or the key is also a partial match for another key since this will match the pattern anywhere in a string. You could call this a fuzzy search. For example, if You're looking for a key named "Jim" and you have a key named "Jimmy" this will return true.
You can limit this by tightening up the pattern matching. This can easily be accomplished by adding a space, which is the separator when treating an array like a string. This can be tricky too. If you add a space before the test key, it will fail if it's the first key. The same is true for the last key if you add the space after. You could test for both separately.
[[ ${!animals[*]} =~ (${test_key} ) || ${!animals[*]} =~ ( ${test_key}) ]] && ... || ...
# ^ ^
The other caveat is if there is only 1 key. If there is only 1 key in the array, then there will be no spaces. You could add some simple logic to test for how many keys and then only test with spaces for key quantities greater than 1.
if [[ ${#animals[@]} -gt 1 ]] ; then
# test with spaces
else
# test without spaces
fi
grep "$test_key " <<< ${!animals[*]} && echo "${test_key} exists" || ( grep " $test_key" <<< ${!animals[*]} && echo "${test_key} exists" || echo "${test_key} doesn't exist" )
You would have to include spaces in your test here as well. I wouldn't use grep, but you could.
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)