Check if a Bash array contains a value

前端 未结 30 2438
执笔经年
执笔经年 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:51

    Using parameter expansion:

    ${parameter:+word} If parameter is null or unset, nothing is substituted, otherwise the expansion of word is substituted.

    declare -A myarray
    myarray[hello]="world"
    
    for i in hello goodbye 123
    do
      if [ ${myarray[$i]:+_} ]
      then
        echo ${!myarray[$i]} ${myarray[$i]} 
      else
        printf "there is no %s\n" $i
      fi
    done
    
    0 讨论(0)
  • 2020-11-22 07:52
    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
    
    0 讨论(0)
  • 2020-11-22 07:52

    Borrowing from Dennis Williamson's answer, the following solution combines arrays, shell-safe quoting, and regular expressions to avoid the need for: iterating over loops; using pipes or other sub-processes; or using non-bash utilities.

    declare -a array=('hello, stack' one 'two words' words last)
    printf -v array_str -- ',,%q' "${array[@]}"
    
    if [[ "${array_str},," =~ ,,words,, ]]
    then
       echo 'Matches'
    else
       echo "Doesn't match"
    fi
    

    The above code works by using Bash regular expressions to match against a stringified version of the array contents. There are six important steps to ensure that the regular expression match can't be fooled by clever combinations of values within the array:

    1. Construct the comparison string by using Bash's built-in printf shell-quoting, %q. Shell-quoting will ensure that special characters become "shell-safe" by being escaped with backslash \.
    2. Choose a special character to serve as a value delimiter. The delimiter HAS to be one of the special characters that will become escaped when using %q; that's the only way to guarantee that values within the array can't be constructed in clever ways to fool the regular expression match. I choose comma , because that character is the safest when eval'd or misused in an otherwise unexpected way.
    3. Combine all array elements into a single string, using two instances of the special character to serve as delimiter. Using comma as an example, I used ,,%q as the argument to printf. This is important because two instances of the special character can only appear next to each other when they appear as the delimiter; all other instances of the special character will be escaped.
    4. Append two trailing instances of the delimiter to the string, to allow matches against the last element of the array. Thus, instead of comparing against ${array_str}, compare against ${array_str},,.
    5. If the target string you're searching for is supplied by a user variable, you must escape all instances of the special character with a backslash. Otherwise, the regular expression match becomes vulnerable to being fooled by cleverly-crafted array elements.
    6. Perform a Bash regular expression match against the string.
    0 讨论(0)
  • 2020-11-22 07:53

    Here's my take on this.

    I'd rather not use a bash for loop if I can avoid it, as that takes time to run. If something has to loop, let it be something that was written in a lower level language than a shell script.

    function array_contains { # arrayname value
      local -A _arr=()
      local IFS=
      eval _arr=( $(eval printf '[%q]="1"\ ' "\${$1[@]}") )
      return $(( 1 - 0${_arr[$2]} ))
    }
    

    This works by creating a temporary associative array, _arr, whose indices are derived from the values of the input array. (Note that associative arrays are available in bash 4 and above, so this function won't work in earlier versions of bash.) We set $IFS to avoid word splitting on whitespace.

    The function contains no explicit loops, though internally bash steps through the input array in order to populate printf. The printf format uses %q to ensure that input data are escaped such that they can safely be used as array keys.

    $ a=("one two" three four)
    $ array_contains a three && echo BOOYA
    BOOYA
    $ array_contains a two && echo FAIL
    $
    

    Note that everything this function uses is a built-in to bash, so there are no external pipes dragging you down, even in the command expansion.

    And if you don't like using eval ... well, you're free to use another approach. :-)

    0 讨论(0)
  • 2020-11-22 07:54

    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=$'\t'
    array=("Jack Brown\tJack Smith")
    unset IFS
    value="Jack"
    
    if [[ "\t${array[@]}\t" =~ "\t${value}\t" ]]; then
        echo "true"
    else
        echo "false"
    fi
    

    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"
    
    0 讨论(0)
  • 2020-11-22 07:54
    containsElement () { for e in "${@:2}"; do [[ "$e" = "$1" ]] && return 0; done; return 1; }
    

    Now handles empty arrays correctly.

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