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


当前回答

@ghostdog74关于使用大小写逻辑检查数组包含特定值的回答的一个小补充:

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

或者打开extglob选项,你可以这样做:

myarray=(one two three)
word=two
shopt -s extglob
case "${myarray[@]}" in ?(*" ")"$word"?(" "*)) echo "found" ;; esac

我们也可以用if语句:

myarray=(one two three)
word=two
if [[ $(printf "_[%s]_" "${myarray[@]}") =~ .*_\[$word\]_.* ]]; then echo "found"; fi

其他回答

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 $*

使用参数展开:

如果参数为空或未设置,则什么都没有 替换,否则词的展开就会被替换。

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

如果你不想重复,这是值得研究的:

#!/bin/bash
myarray=("one" "two" "three");
wanted="two"
if `echo ${myarray[@]/"$wanted"/"WAS_FOUND"} | grep -q "WAS_FOUND" ` ; then
 echo "Value was found"
fi
exit

片段改编自:http://www.thegeekstuff.com/2010/06/bash-array-tutorial/ 我认为这很聪明。

编辑: 你可以这样做:

if `echo ${myarray[@]} | grep -q "$wanted"` ; then
echo "Value was found"
fi

但后者仅在数组包含唯一值时有效。我认为,在143中寻找1只会给出假阳性。

结合Beorn Harris和loentar的回答,我们得出了一个更有趣的单行测试:

delim=$'\x1F' # define a control code to be used as more or less reliable delimiter
if [[ "${delim}${array[@]}${delim}" =~ "${delim}a string to test${delim}" ]]; then
    echo "contains 'a string to test'"
fi

它不使用额外的函数,不替换测试,并添加了额外的保护,防止使用控制代码作为分隔符偶尔出现错误匹配。


UPD:感谢@ChrisCogdon的注意,这个错误的代码被重写并以https://stackoverflow.com/a/58527681/972463的形式发布。

如何检查一个Bash数组是否包含一个值


假阳性匹配

array=(a1 b1 c1 d1 ee)

[[ ${array[*]} =~ 'a' ]] && echo 'yes' || echo 'no'
# output:
yes

[[ ${array[*]} =~ 'a1' ]] && echo 'yes' || echo 'no'
# output:
yes

[[ ${array[*]} =~ 'e' ]] && echo 'yes' || echo 'no'
# output:
yes

[[ ${array[*]} =~ 'ee' ]] && echo 'yes' || echo 'no'
# output:
yes

精确匹配

为了寻找精确匹配,你的正则表达式模式需要在值的前后添加额外的空格,如(^|[[:space:]])" value "($|[[:space:]])

# Exact match

array=(aa1 bc1 ac1 ed1 aee)

if [[ ${array[*]} =~ (^|[[:space:]])"a"($|[[:space:]]) ]]; then
    echo "Yes";
else
    echo "No";
fi
# output:
No

if [[ ${array[*]} =~ (^|[[:space:]])"ac1"($|[[:space:]]) ]]; then
    echo "Yes";
else
    echo "No";
fi
# output:
Yes

find="ac1"
if [[ ${array[*]} =~ (^|[[:space:]])"$find"($|[[:space:]]) ]]; then
    echo "Yes";
else
    echo "No";
fi
# output:
Yes

有关更多用法示例,示例的来源在这里