在Bash中,测试数组是否包含某个值的最简单方法是什么?


当前回答

考虑到:

array=("something to search for" "a string" "test2000")
elem="a string"

然后简单检查一下:

if c=$'\x1E' && p="${c}${elem} ${c}" && [[ ! "${array[@]/#/${c}} ${c}" =~ $p ]]; then
  echo "$elem exists in array"
fi

在哪里

c is element separator
p is regex pattern

(单独分配p,而不是直接在[[]]中使用表达式的原因是为了保持bash 4的兼容性)

其他回答

这种方法的优点是不需要遍历所有元素(至少不是显式地)。但是由于array.c中的array_to_string_internal()仍然循环遍历数组元素并将它们连接到一个字符串中,因此它可能并不比所提出的循环解决方案更有效,但它更具可读性。

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

请注意,如果您正在搜索的值是带有空格的数组元素中的某个单词,则会给出假阳性。例如

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

正则表达式将“Jack”视为在数组中,即使它不在数组中。所以你必须改变IFS和正则表达式上的分隔符如果你仍然想使用这个解决方案,就像这样

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

这将打印“false”。

显然,这也可以用作测试语句,允许将其表示为一行程序

[[ " ${array[*]} " =~ " ${value} " ]] && echo "true" || echo "false"
for i in "${array[@]}"
do
    if [ "$i" -eq "$yourValue" ] ; then
        echo "Found"
    fi
done

字符串:

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

下面的代码检查给定值是否在数组中,并返回其从零开始的偏移量:

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

匹配是在完整的值上完成的,因此设置VALUE="three"将不匹配。

以下是我对这个问题的看法。以下是简短的版本:

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

长一点的版本,我觉得看起来更舒服。

# 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$"
}

例子:

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

如果您想做一个快速而复杂的测试,看看是否值得遍历整个数组以获得精确匹配,Bash可以像对待标量一样对待数组。测试标量中的匹配项,如果没有,则跳过循环节省时间。显然你会得到假阳性。

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

这将输出“Checking”和“Match”。使用array=(word "two words" something),它只会输出"Checking"。使用array=(单词“two widgets”什么的)将没有输出。