Is there a nice way of checking if an array has an element in bash (better than looping through)?

Alternatively, is there another way to check if a number or string equals any of a set of predefined constants?

link|improve this question

50% accept rate
feedback

4 Answers

up vote 5 down vote accepted

In Bash 4, you can use associative arrays:

# set up array of constants
declare -A array
for constant in foo bar baz
do
    array[$constant]=1
done

# test for existence
test1="bar"
test2="xyzzy"

if [[ ${array[$test1]} ]]; then echo "Exists"; fi    # Exists
if [[ ${array[$test2]} ]]; then echo "Exists"; fi    # doesn't

To set up the array initially you could also do direct assignments:

array[foo]=1
array[bar]=1
# etc.

or this way:

array=([foo]=1 [bar]=1 [baz]=1)
link|improve this answer
Actually, the [[]] test doesn't work in the case the value is empty. E.g., "array['test']=''". In this case, the key 'test' exists, and you can see it listed with ${!array[@]}, but "[[ ${array['test']} ]]; echo $?" echoes 1, not 0. – haridsv Jun 6 '11 at 1:59
${array[$test1]} is simple but has a problem: it won't work if you use set -u in your scripts (which is recommended), as you'd get "unbound variable". – tokland May 25 at 21:53
@tokland: Who recommends it? I certainly don't. – Dennis Williamson May 25 at 22:00
@DennisWilliamson: Ok, some people recommend it, but I think it would be nice to have a solution that works regardless the value of these flags. – tokland May 25 at 22:03
feedback

There is a way to test if an element of an associative array exists (not set), this is different from empty:

isNotSet() {
    if [[ ! ${!1} && ${!1-_} ]]
    then
        return 1
    fi
}

Then use it:

declare -A assoc
KEY="key"
isNotSet assoc[${KEY}]
if [ $? -ne 0 ]
then
  echo "${KEY} is not set."
fi
link|improve this answer
just a note: declare -A doesn't work on bash 3.2.39 (debian lenny), but it works on bash 4.1.5 (debian squeeze) – Reef Dec 14 '11 at 16:05
Associative arrays were introduced in Bash 4. – Diego F. Durán Dec 20 '11 at 15:23
note that if ! some_check then return 1 = some_check. So: isNotSet() { [[ ... ]] }. Check my solution below, you can do it in a simple check. – tokland May 25 at 22:16
feedback

It's an old question, but I think what is the simplest solution has not appeared yet: test ${array[key]+_}. Example:

declare -A xs=([a]=1 [b]="")
test ${xs[a]+_} && echo "a is set"
test ${xs[b]+_} && echo "b is set"
test ${xs[c]+_} && echo "c is set"

Outputs:

a is set
b is set

To see how this work check this.

link|improve this answer
feedback

I don't think you can do it properly without looping unless you have very limited data in the array.

Here is one simple variant, this would correctly say that "Super User" exists in the array. But it would also say that "uper Use" is in the array.

MyArray=('Super User' 'Stack Overflow' 'Server Fault' 'Jeff' );
FINDME="Super User"

FOUND=`echo ${MyArray[*]} | grep "$FINDME"`

if [ "${FOUND}" != "" ]; then
  echo Array contains: $FINDME
else
  echo $FINDME not found
fi

#
# If you where to add anchors < and > to the data it could work
# This would find "Super User" but not "uper Use"
#

MyArray2=('<Super User>' '<Stack Overflow>' '<Server Fault>' '<Jeff>' );

FOUND=`echo ${MyArray2[*]} | grep "<$FINDME>"`

if [ "${FOUND}" != "" ]; then
  echo Array contains: $FINDME
else
  echo $FINDME not found
fi

The problem is that there is no easy way to add the anchors (that I can think of) besides looping through the array. Unless you can add them before you put them in the array...

link|improve this answer
It is a nice solution when the constants are alphanumeric, though (with grep "\b$FINDME\b"). Probably could work with non-alphanumeric constants that have no spaces, with "(^| )$FINDME(\$| )" (or something like that... I have never been able to learn what flavor of regexp grep uses.) – Tgr Nov 30 '10 at 11:10
feedback

Your Answer

 
or
required, but never shown

Not the answer you're looking for? Browse other questions tagged or ask your own question.