在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,你指出了数组项中可以存在换行符的情况。
其他回答
另一个没有函数的代码:
(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"
我通常编写这类实用程序来操作变量的名称,而不是变量的值,这主要是因为bash不能通过引用传递变量。
下面是一个使用数组名称的版本:
function array_contains # array value
{
[[ -n "$1" && -n "$2" ]] || {
echo "usage: array_contains <array> <value>"
echo "Returns 0 if array contains value, 1 otherwise"
return 2
}
eval 'local values=("${'$1'[@]}")'
local element
for element in "${values[@]}"; do
[[ "$element" == "$2" ]] && return 0
done
return 1
}
这样,问题示例就变成:
array_contains A "one" && echo "contains one"
etc.
考虑到:
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的兼容性)
以下是我对这个问题的看法。以下是简短的版本:
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
我的版本的正则表达式技术,已经建议:
values=(foo bar)
requestedValue=bar
requestedValue=${requestedValue##[[:space:]]}
requestedValue=${requestedValue%%[[:space:]]}
[[ "${values[@]/#/X-}" =~ "X-${requestedValue}" ]] || echo "Unsupported value"
What's happening here is that you're expanding the entire array of supported values into words and prepending a specific string, "X-" in this case, to each of them, and doing the same to the requested value. If this one is indeed contained in the array, then the resulting string will at most match one of the resulting tokens, or none at all in the contrary. In the latter case the || operator triggers and you know you're dealing with an unsupported value. Prior to all of that the requested value is stripped of all leading and trailing whitespace through standard shell string manipulation.
我相信它是干净而优雅的,尽管如果支持的值数组特别大,我不太确定它的性能如何。