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


当前回答

扩展上面来自Sean DiSanti的答案,我认为下面是一个简单而优雅的解决方案,它避免了对数组进行循环,并且不会由于部分匹配而给出假阳性

function is_in_array {
    local ELEMENT="${1}"
    local DELIM=","
    printf "${DELIM}%s${DELIM}" "${@:2}" | grep -q "${DELIM}${ELEMENT}${DELIM}"
}

可以这样称呼:

$ haystack=("needle1" "needle2" "aneedle" "spaced needle")
$ is_in_array "needle" "${haystack[@]}"
$ echo $?
1
$ is_in_array "needle1" "${haystack[@]}"
$ echo $?
0

其他回答

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

The answer with most votes is very concise and clean, but it can have false positives when a space is part of one of the array elements. This can be overcome when changing IFS and using "${array[*]}" instead of "${array[@]}". The method is identical, but it looks less clean. By using "${array[*]}", we print all elements of $array, separated by the first character in IFS. So by choosing a correct IFS, you can overcome this particular issue. In this particular case, we decide to set IFS to an uncommon character $'\001' which stands for Start of Heading (SOH)

$ array=("foo bar" "baz" "qux")
$ IFS=$'\001'
$ [[ "$IFS${array[*]}$IFS" =~ "${IFS}foo${IFS}" ]] && echo yes || echo no
no
$ [[ "$IFS${array[*]}$IFS" =~ "${IFS}foo bar${IFS}" ]] && echo yes || echo no
yes
$ unset IFS

这解决了大多数假阳性问题,但需要一个好的IFS选择。

注意:如果之前设置了IFS,最好保存并重新设置,而不是使用未设置的IFS


相关:

访问bash命令行参数$@ vs $*

一行的解决方案

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,你指出了数组项中可以存在换行符的情况。

另一个没有函数的代码:

(for e in "${array[@]}"; do [[ "$e" == "searched_item" ]] && exit 0; done) && echo "found" || echo "not found"

谢谢@Qwerty关于空格的提示!

对应的功能:

find_in_array() {
  local word=$1
  shift
  for e in "$@"; do [[ "$e" == "$word" ]] && return 0; done
  return 1
}

例子:

some_words=( these are some words )
find_in_array word "${some_words[@]}" || echo "expected missing! since words != word"

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

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