在Bash中,测试数组是否包含某个值的最简单方法是什么?
当前回答
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 $*
其他回答
结合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的形式发布。
考虑到:
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的兼容性)
我看到了一些解决这个问题的方法。
对于使用grep的索引数组
grep ${value} <<< ${array[*]} && true || false
对于使用grep的关联数组键
grep ${value} <<< "${!array[*]}" && true || false
我们可以使用awk,但它可能太过了。
awk --assign "v=${value}" '$v~$0 {print true}' <<<"${!array[*]}
Case语句。
case "${array[*]}" in (*${value}*) true ;; (*) false ;; esac
Bash条件表达式在ksh88风格双方括号:
[[ ${array[@]} =~ ${value} ]] && true || false
注意:顺序很重要,正则表达式在=~ match运算符的右边。
Bash for循环
for ((i=0;i<"${#array[*]}";i++)) ; [[ ${array[i]} = $value ]] && break 0 &> /dev/null || continue; done
注意,在这种特殊情况下,真逻辑是颠倒的,即1=真,0=假。这是因为我们使用break 0强制break内置除true之外的退出代码,除非break n参数小于1,否则总是如此。我们必须要打破循环,我们想要一个布尔退出码除了默认的'true',所以在这种情况下,我们翻转了逻辑。因此,使用具有返回true语义的函数可能更有意义。
containsElement () { for e in "${@:2}"; do [[ "$e" = "$1" ]] && return 0; done; return 1; }
现在正确处理空数组。
使用参数展开:
如果参数为空或未设置,则什么都没有 替换,否则词的展开就会被替换。
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