Test if element is in array in bash

130,633

Solution 1

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)

Solution 2

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.

Solution 3

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

Solution 4

You can see if an entry is present by piping the contents of the array to grep.

 printf "%s\n" "${mydata[@]}" | grep "^${val}$"

You can also get the index of an entry with grep -n, which returns the line number of a match (remember to subtract 1 to get zero-based index) This will be reasonably quick except for very large arrays.

# given the following data
mydata=(a b c "hello world")
for val in a c hello "hello world"
do
           # get line # of 1st matching entry
    ix=$( printf "%s\n" "${mydata[@]}" | grep -n -m 1 "^${val}$" | cut -d ":" -f1 )
    if [[ -z $ix ]]
    then
        echo $val missing
    else
         # subtract 1.  Bash arrays are zero-based, but grep -n returns 1 for 1st line, not 0 
        echo $val found at $(( ix-1 ))
    fi
done
a found at 0
c found at 2
hello missing
hello world found at 3

explanation:

  • $( ... ) is the same as using backticks to capture output of a command into a variable
  • printf outputs mydata one element per line
  • (all quotes necessary, along with @ instead of *. this avoids splitting "hello world" into 2 lines)
  • grep searches for exact string: ^ and $ match beginning and end of line
  • grep -n returns line #, in form of 4:hello world
  • grep -m 1 finds first match only
  • cut extracts just the line number
  • subtract 1 from returned line number.

You can of course fold the subtraction into the command. But then test for -1 for missing:

ix=$(( $( printf "%s\n" "${mydata[@]}" | grep -n -m 1 "^${val}$" | cut -d ":" -f1 ) - 1 ))
if [[ $ix == -1 ]]; then echo missing; else ... fi
  • $(( ... )) does integer arithmetic

Solution 5

#!/bin/bash
function in_array {
  ARRAY=$2
  for e in ${ARRAY[*]}
  do
    if [[ "$e" == "$1" ]]
    then
      return 0
    fi
  done
  return 1
}
my_array=(Drupal Wordpress Joomla)
if in_array "Drupal" "${my_array[*]}"
  then
    echo "Found"
  else
    echo "Not found"
fi
Share:
130,633

Related videos on Youtube

Tgr
Author by

Tgr

Updated on September 17, 2022

Comments

  • Tgr
    Tgr 9 months

    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?

  • Tgr
    Tgr over 12 years
    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.)
  • haridsv
    haridsv about 12 years
    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.
  • Diego Fernández Durán over 11 years
    Associative arrays were introduced in Bash 4.
  • tokland
    tokland about 11 years
    ${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".
  • Dennis Williamson
    Dennis Williamson about 11 years
    @tokland: Who recommends it? I certainly don't.
  • tokland
    tokland about 11 years
    @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
    tokland about 11 years
  • tokland
    tokland about 11 years
    note that if ! some_check then return 1 = some_check. So: isNotSet() { [[ ... ]] }. Check my solution below, you can do it in a simple check.
  • Adam D.
    Adam D. over 7 years
    The info manual recommneds you to use env to avoid ambiguities in aliases, progs and other functions that may have adopted the name "test". As above env test ${xs[a]+_} && echo "a is set". You can also get this functionality using double-brackets, the same trick then checking for null: [[ ! -z "${xs[b]+_}" ]] && echo "b is set"
  • bertieb
    bertieb over 5 years
    Can you elaborate on why you are suggesting this approach? OP asked if there is a way to do it without looping through the array, which is what you are doing in in_array. Cheers
  • Tobias
    Tobias over 4 years
    Well, at least that loop is capsuled in a function, which might be good enough for many cases (with small data sets), and doesn't require bash 4+. Probably ${ARRAY[@]} should be used.
  • Arne L.
    Arne L. almost 4 years
    Also you can use the even simpler [[ ${xs[b]+set} ]]