Check if an element is present in a Bash array

2019-01-10 16:24发布

问题:

I was wondering if there is an efficient way to check if an element is present within an array in Bash? I am looking for something similar to what I can do in Python, like:

arr = ['a','b','c','d']

if 'd' in arr:
    do your thing
else:
    do something

I've seen solutions using associative array for bash for Bash 4+, but I am wondering if there is another solution out there.

Please understand that I know the trivial solution is to iterate in the array, but I don't want that.

回答1:

You could do:

if [[ " ${arr[*]} " == *" d "* ]]; then
    echo "arr contains d"
fi

This will give false positives for example if you look for "a b" -- that substring is in the joined string but not as an array element. This dilemma will occur for whatever delimiter you choose.

The safest way is to loop over the array until you find the element:

array_contains () {
    local seeking=$1; shift
    local in=1
    for element; do
        if [[ $element == $seeking ]]; then
            in=0
            break
        fi
    done
    return $in
}

arr=(a b c "d e" f g)
array_contains "a b" "${arr[@]}" && echo yes || echo no    # no
array_contains "d e" "${arr[@]}" && echo yes || echo no    # yes

Here's a "cleaner" version where you just pass the array name, not all its elements

array_contains2 () { 
    local array="$1[@]"
    local seeking=$2
    local in=1
    for element in "${!array}"; do
        if [[ $element == $seeking ]]; then
            in=0
            break
        fi
    done
    return $in
}

array_contains2 arr "a b"  && echo yes || echo no    # no
array_contains2 arr "d e"  && echo yes || echo no    # yes


回答2:

Obvious caveats aside, if your array was actually like the one above, you could do

if [[ ${arr[*]} =~ d ]]
then
  do your thing
else
  do something
fi


回答3:

If array elements don't contain spaces, another (perhaps more readable) solution would be:

if echo ${arr[@]} | grep -q -w "d"; then 
    echo "is in array"
else 
    echo "is not in array"
fi


回答4:

1) Initialize array arr and add elements

2) set variable to search for SEARCH_STRING

3) check if your array contains element

arr=()
arr+=('a')
arr+=('b')
arr+=('c')

SEARCH_STRING='b'

if [[ " ${arr[*]} " == *"$SEARCH_STRING"* ]];
then
    echo "YES, your arr contains $SEARCH_STRING"
else
    echo "NO, your arr does not contain $SEARCH_STRING"
fi


回答5:

Here's another way that might be faster, in terms of compute time, than iterating. Not sure. The idea is to convert the array to a string, truncate it, and get the size of the new array.

For example, to find the index of 'd':

arr=(a b c d)    
temp=`echo ${arr[@]}`
temp=( ${temp%%d*} )
index=${#temp[@]}

You could turn this into a function like:

get-index() {

    Item=$1
    Array="$2[@]"

    ArgArray=( ${!Array} )
    NewArray=( ${!Array%%${Item}*} )

    Index=${#NewArray[@]}

    [[ ${#ArgArray[@]} == ${#NewArray[@]} ]] && echo -1 || echo $Index

}

You could then call:

get-index d arr

and it would echo back 3, which would be assignable with:

index=`get-index d arr`


回答6:

FWIW, here's what I used:

expr "${arr[*]}" : ".*\<$item\>"

This works where there are no delimiters in any of the array items or in the search target. I didn't need to solve the general case for my applicaiton.



回答7:

array=("word" "two words") # let's look for "two words"

using grep and printf:

(printf '%s\n' "${array[@]}" | grep -x -q "two words") && <run_your_if_found_command_here>

using for:

(for e in "${array[@]}"; do [[ "$e" == "two words" ]] && exit 0; done; exit 1) && <run_your_if_found_command_here>

For not_found results add || <run_your_if_notfound_command_here>