Check if a Bash array contains a value

前端 未结 30 2410
执笔经年
执笔经年 2020-11-22 07:14

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

相关标签:
30条回答
  • 2020-11-22 07:55

    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"
    
    0 讨论(0)
  • 2020-11-22 07:55

    Here is my take on this problem. Here is the short version:

    function arrayContains() {
            local haystack=${!1}
            local needle="$2"
            printf "%s\n" ${haystack[@]} | grep -q "^$needle$"
    }
    

    And the long version, which I think is much easier on the eyes.

    # With added utility function.
    function arrayToLines() {
            local array=${!1}
            printf "%s\n" ${array[@]}
    }
    
    function arrayContains() {
            local haystack=${!1}
            local needle="$2"
            arrayToLines haystack[@] | grep -q "^$needle$"
    }
    

    Examples:

    test_arr=("hello" "world")
    arrayContains test_arr[@] hello; # True
    arrayContains test_arr[@] world; # True
    arrayContains test_arr[@] "hello world"; # False
    arrayContains test_arr[@] "hell"; # False
    arrayContains test_arr[@] ""; # False
    
    0 讨论(0)
  • 2020-11-22 07:57

    A small addition to @ghostdog74's answer about using case logic to check that array contains particular value:

    myarray=(one two three)
    word=two
    case "${myarray[@]}" in  ("$word "*|*" $word "*|*" $word") echo "found" ;; esac
    

    Or with extglob option turned on, you can do it like this:

    myarray=(one two three)
    word=two
    shopt -s extglob
    case "${myarray[@]}" in ?(*" ")"$word"?(" "*)) echo "found" ;; esac
    

    Also we can do it with if statement:

    myarray=(one two three)
    word=two
    if [[ $(printf "_[%s]_" "${myarray[@]}") =~ .*_\[$word\]_.* ]]; then echo "found"; fi
    
    0 讨论(0)
  • 2020-11-22 08:00

    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"
    
    0 讨论(0)
  • 2020-11-22 08:01

    The following code checks if a given value is in the array and returns its zero-based offset:

    A=("one" "two" "three four")
    VALUE="two"
    
    if [[ "$(declare -p A)" =~ '['([0-9]+)']="'$VALUE'"' ]];then
      echo "Found $VALUE at offset ${BASH_REMATCH[1]}"
    else
      echo "Couldn't find $VALUE"
    fi
    

    The match is done on the complete values, therefore setting VALUE="three" would not match.

    0 讨论(0)
  • 2020-11-22 08:02

    I had the case that I had to check if an ID was contained in a list of IDs generated by another script / command. For me worked the following:

    # the ID I was looking for
    ID=1
    
    # somehow generated list of IDs
    LIST=$( <some script that generates lines with IDs> )
    # list is curiously concatenated with a single space character
    LIST=" $LIST "
    
    # grep for exact match, boundaries are marked as space
    # would therefore not reliably work for values containing a space
    # return the count with "-c"
    ISIN=$(echo $LIST | grep -F " $ID " -c)
    
    # do your check (e. g. 0 for nothing found, everything greater than 0 means found)
    if [ ISIN -eq 0 ]; then
        echo "not found"
    fi
    # etc.
    

    You could also shorten / compact it like this:

    if [ $(echo " $( <script call> ) " | grep -F " $ID " -c) -eq 0 ]; then
        echo "not found"
    fi
    

    In my case, I was running jq to filter some JSON for a list of IDs and had to later check if my ID was in this list and this worked the best for me. It will not work for manually created arrays of the type LIST=("1" "2" "4") but for with newline separated script output.


    PS.: could not comment an answer because I'm relatively new ...

    0 讨论(0)
提交回复
热议问题