在Bash中,测试数组是否包含某个值的最简单方法是什么?
当前回答
如果你需要性能,你不希望每次搜索时都要遍历整个数组。
在这种情况下,您可以创建一个表示该数组索引的关联数组(哈希表或字典)。也就是说,它将每个数组元素映射到它在数组中的索引:
make_index () {
local index_name=$1
shift
local -a value_array=("$@")
local i
# -A means associative array, -g means create a global variable:
declare -g -A ${index_name}
for i in "${!value_array[@]}"; do
eval ${index_name}["${value_array[$i]}"]=$i
done
}
然后你可以这样使用它:
myarray=('a a' 'b b' 'c c')
make_index myarray_index "${myarray[@]}"
并像这样测试成员:
member="b b"
# the "|| echo NOT FOUND" below is needed if you're using "set -e"
test "${myarray_index[$member]}" && echo FOUND || echo NOT FOUND
或者:
if [ "${myarray_index[$member]}" ]; then
echo FOUND
fi
请注意,即使在测试值或数组值中存在空格,该解决方案也能正确执行。
作为奖励,您还可以通过以下方式获得数组中值的索引:
echo "<< ${myarray_index[$member]} >> is the index of $member"
其他回答
for i in "${array[@]}"
do
if [ "$i" -eq "$yourValue" ] ; then
echo "Found"
fi
done
字符串:
for i in "${array[@]}"
do
if [ "$i" == "$yourValue" ] ; then
echo "Found"
fi
done
以下是我对这个问题的看法。以下是简短的版本:
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
结合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=(word "two words" words)
if [[ ${array[@]} =~ words ]]
then
echo "Checking"
for element in "${array[@]}"
do
if [[ $element == "words" ]]
then
echo "Match"
fi
done
fi
这将输出“Checking”和“Match”。使用array=(word "two words" something),它只会输出"Checking"。使用array=(单词“two widgets”什么的)将没有输出。
$ myarray=(one two three)
$ case "${myarray[@]}" in *"two"*) echo "found" ;; esac
found