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


当前回答

一行的解决方案

printf '%s\0' "${myarray[@]}" | grep -F -x -z -- 'myvalue'

解释

printf语句打印数组中的每个元素,以空字符分隔。

grep语句使用以下标志来匹配一个包含myvalue字符串的项(不多不少):

-z/——null-data -行以0字节而不是换行符结束。 -f /——fixed-strings -将pattern解释为固定字符串,而不是正则表达式。 -x/——line-regexp -只选择与整行完全匹配的匹配项。 ——-标记命令行选项的结束,使Grep处理“myvalue”作为一个非选项参数,即使它以破折号开始

为什么我们使用空字节\0而不是换行符\n?数组的元素实际上可能包含换行符。(如果您知道它没有,请随意删除-z grep选项,并将%s\n替换为您的第一个printf参数。)


使用

把这个放进一个if…然后声明:

if printf '%s\0' "${myarray[@]}" | grep -Fxqz -- 'myvalue'; then
    # ...
fi

我在grep表达式中添加了-q标志,这样它就不会打印匹配项;它只会将匹配的存在视为“真”。

更新:感谢presto8指出——line-regexp标志。谢谢Tino,你指出了数组项中可以存在换行符的情况。

其他回答

我通常只使用:

inarray=$(echo ${haystack[@]} | grep -o "needle" | wc -w)

非零值表示找到了匹配。

... 实际上,为了解决它不能与needle1和needle2工作的问题,如果你只想要一个精确匹配,没有更多,没有更少,只需在-o后面添加一个w标志,用于整个单词匹配:

inarray=$(echo ${haystack[@]} | grep -ow "needle" | wc -w)

考虑到:

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的兼容性)

虽然这里有几个很好的和有用的答案,但我没有找到一个似乎是性能、跨平台和健壮性的正确组合;所以我想分享一下我为我的代码编写的解决方案:

#!/bin/bash

# array_contains "$needle" "${haystack[@]}"
#
# Returns 0 if an item ($1) is contained in an array ($@).
#
# Developer note:
#    The use of a delimiter here leaves something to be desired. The ideal
#    method seems to be to use `grep` with --line-regexp and --null-data, but
#    Mac/BSD grep doesn't support --line-regexp.
function array_contains()
{
    # Extract and remove the needle from $@.
    local needle="$1"
    shift

    # Separates strings in the array for matching. Must be extremely-unlikely
    # to appear in the input array or the needle.
    local delimiter='#!-\8/-!#'

    # Create a string with containing every (delimited) element in the array,
    # and search it for the needle with grep in fixed-string mode.
    if printf "${delimiter}%s${delimiter}" "$@" | \
        grep --fixed-strings --quiet "${delimiter}${needle}${delimiter}"; then
        return 0
    fi

    return 1
}

下面是实现这一点的一个小函数。搜索字符串是第一个参数,其余是数组元素:

set +e #otherwise the script will exit on error
containsElement () {
  local e match="$1"
  shift
  for e; do [[ "$e" == "$match" ]] && return 0; done
  return 1
}

该函数的测试运行如下:

$ array=("something to search for" "a string" "test2000")
$ containsElement "a string" "${array[@]}"
$ echo $?
0
$ containsElement "blaha" "${array[@]}"
$ echo $?
1

保持简单:

Array1=( "item1" "item2" "item3" "item-4" )
var="item3"

count=$(echo ${Array1[@]} | tr ' ' '\n' | awk '$1 == "'"$var"'"{print $0}' | wc -l)
[ $count -eq 0 ] && echo "Not found" || echo "found"