Check If a Bash Array Contains a Value

Check If a Bash Array Contains a Value

In Bash, what is the simplest way to test if an array contains a certain value?

0

41 Answers

This approach has the advantage of not needing to loop over all the elements (at least not explicitly). But since array_to_string_internal() in array.c still loops over array elements and concatenates them into a string, it's probably not more efficient than the looping solutions proposed, but it's more readable.

if [[ " ${array[*]} " =~ " ${value} " ]]; then
    # whatever you want to do when array contains value
fi

if [[ ! " ${array[*]} " =~ " ${value} " ]]; then
    # whatever you want to do when array doesn't contain value
fi

Note that in cases where the value you are searching for is one of the words in an array element with spaces, it will give false positives. For example

array=("Jack Brown")
value="Jack"

The regex will see "Jack" as being in the array even though it isn't. So you'll have to change IFS and the separator characters on your regex if you want still to use this solution, like this

IFS="|"
array=("Jack Brown${IFS}Jack Smith")
value="Jack"

if [[ "${IFS}${array[*]}${IFS}" =~ "${IFS}${value}${IFS}" ]]; then
    echo "true"
else
    echo "false"
fi

unset IFS # or set back to original IFS if previously set

This will print "false".

Obviously this can also be used as a test statement, allowing it to be expressed as a one-liner

[[ " ${array[*]} " =~ " ${value} " ]] && echo "true" || echo "false"
20

Below is a small function for achieving this. The search string is the first argument and the rest are the array elements:

containsElement () {
  local e match="$1"
  shift
  for e; do [[ "$e" == "$match" ]] && return 0; done
  return 1
}

A test run of that function could look like:

$ array=("something to search for" "a string" "test2000")
$ containsElement "a string" "${array[@]}"
$ echo $?
0
$ containsElement "blaha" "${array[@]}"
$ echo $?
1
26

One-line solution

printf '%s\0' "${myarray[@]}" | grep -F -x -z 'myvalue'

Explanation

The printf statement prints each element of the array, delimited by null characters.

The grep statement uses the following flags to match an item that contains exactly the string given as myvalue (no more, no less):

  • -z/--null-data - Lines are terminated by a zero byte instead of a newline.
  • -F/--fixed-strings - Interpret PATTERNS as fixed strings, not regular expressions.
  • -x/--line-regexp - Select only those matches that exactly match the whole line.

Why do we use a null byte \0 instead of a newline \n? Your array may actually contain newlines within its elements. (If you know that it doesn't, feel free to drop the -z grep option and substitude %s\n as your first printf arg.)


Usage

To put this into an if ... then statement:

if printf '%s\0' "${myarray[@]}" | grep -Fxqz 'myvalue'; then
    # ...
fi

I added a -q flag to the grep expression so that it won't print matches; it will just treat the existence of a match as "true."

Update: Thanks, presto8, for pointing out the --line-regexp flag. Thanks, Tino, for pointing out the case where newlines can exist within array items.

8
for i in "${array[@]}"
do
    if [ "$i" -eq "$yourValue" ] ; then
        echo "Found"
    fi
done

For strings:

for i in "${array[@]}"
do
    if [ "$i" == "$yourValue" ] ; then
        echo "Found"
    fi
done
6
$ myarray=(one two three)
$ case "${myarray[@]}" in  *"two"*) echo "found" ;; esac
found
4

I typically just use:

inarray=$(echo ${haystack[@]} | grep -o "needle" | wc -w)

non zero value indicates a match was found.

... actually, to solve the problem mentioned with it not working with needle1 and needle2, if you only want an exact match, nothing more, nothing less, just add a w after the -o for a whole word match

inarray=$(echo ${haystack[@]} | grep -ow "needle" | wc -w)

6

If you need performance, you don't want to loop over your whole array every time you search.

In this case, you can create an associative array (hash table, or dictionary) that represents an index of that array. I.e. it maps each array element into its index in the array:

make_index () {
  local index_name=$1
  shift
  local -a value_array=("$@")
  local i
  # -A means associative array, -g means create a global variable:
  declare -g -A ${index_name}
  for i in "${!value_array[@]}"; do
    eval ${index_name}["${value_array[$i]}"]=$i
  done
}

Then you can use it like this:

myarray=('a a' 'b b' 'c c')
make_index myarray_index "${myarray[@]}"

And test membership like so:

member="b b"
# the "|| echo NOT FOUND" below is needed if you're using "set -e"
test "${myarray_index[$member]}" && echo FOUND || echo NOT FOUND

Or also:

if [ "${myarray_index[$member]}" ]; then 
  echo FOUND
fi

Notice that this solution does the right thing even if the there are spaces in the tested value or in the array values.

As a bonus, you also get the index of the value within the array with:

echo "<< ${myarray_index[$member]} >> is the index of $member"
2

Another one liner without a function:

(for e in "${array[@]}"; do [[ "$e" == "searched_item" ]] && exit 0; done) && echo "found" || echo "not found"

Thanks @Qwerty for the heads up regarding spaces!

corresponding function:

find_in_array() {
  local word=$1
  shift
  for e in "$@"; do [[ "$e" == "$word" ]] && return 0; done
  return 1
}

example:

some_words=( these are some words )
find_in_array word "${some_words[@]}" || echo "expected missing! since words != word"
3
containsElement () { for e in "${@:2}"; do [[ "$e" = "$1" ]] && return 0; done; return 1; }

Now handles empty arrays correctly.

3

If you want to do a quick and dirty test to see if it's worth iterating over the whole array to get a precise match, Bash can treat arrays like scalars. Test for a match in the scalar, if none then skipping the loop saves time. Obviously you can get false positives.

array=(word "two words" words)
if [[ ${array[@]} =~ words ]]
then
    echo "Checking"
    for element in "${array[@]}"
    do
        if [[ $element == "words" ]]
        then
            echo "Match"
        fi
    done
fi

This will output "Checking" and "Match". With array=(word "two words" something) it will only output "Checking". With array=(word "two widgets" something) there will be no output.

3

Here is a small contribution :

array=(word "two words" words)  
search_string="two"  
match=$(echo "${array[@]:0}" | grep -o $search_string)  
[[ ! -z $match ]] && echo "found !"  

Note: this way doesn't distinguish the case "two words" but this is not required in the question.

3

How to check if a Bash Array contains a value


False positive match

array=(a1 b1 c1 d1 ee)

[[ ${array[*]} =~ 'a' ]] && echo 'yes' || echo 'no'
# output:
yes

[[ ${array[*]} =~ 'a1' ]] && echo 'yes' || echo 'no'
# output:
yes

[[ ${array[*]} =~ 'e' ]] && echo 'yes' || echo 'no'
# output:
yes

[[ ${array[*]} =~ 'ee' ]] && echo 'yes' || echo 'no'
# output:
yes
Elena Rostova
Author

Elena Rostova

Elena Rostova holds a Master's degree in Public Health Journalism. She covers groundbreaking medical research, holistic wellness trends, mental health awareness, and nutritional science.