在Bash中,测试数组是否包含某个值的最简单方法是什么?
当前回答
我看到了一些解决这个问题的方法。
对于使用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语义的函数可能更有意义。
其他回答
这是一个小小的贡献:
array=(word "two words" words)
search_string="two"
match=$(echo "${array[@]:0}" | grep -o $search_string)
[[ ! -z $match ]] && echo "found !"
注意:这种方法不区分大小写“两个单词”,但在问题中不需要这样做。
@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
如果您想做一个快速而复杂的测试,看看是否值得遍历整个数组以获得精确匹配,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”什么的)将没有输出。
我有这样的情况,我必须检查一个ID是否包含在另一个脚本/命令生成的ID列表中。 我的工作如下:
# the ID I was looking for
ID=1
# somehow generated list of IDs
LIST=$( <some script that generates lines with IDs> )
# list is curiously concatenated with a single space character
LIST=" $LIST "
# grep for exact match, boundaries are marked as space
# would therefore not reliably work for values containing a space
# return the count with "-c"
ISIN=$(echo $LIST | grep -F " $ID " -c)
# do your check (e. g. 0 for nothing found, everything greater than 0 means found)
if [ ISIN -eq 0 ]; then
echo "not found"
fi
# etc.
你也可以像这样缩短/压缩它:
if [ $(echo " $( <script call> ) " | grep -F " $ID " -c) -eq 0 ]; then
echo "not found"
fi
在我的例子中,我正在运行jq来过滤一些JSON的ID列表,然后必须检查我的ID是否在这个列表中,这对我来说是最好的。 它不适用于手动创建的LIST=("1" "2" "4")类型的数组,而是用于换行分隔的脚本输出。
附言:不能评论一个答案,因为我是相对较新的…
如果你需要性能,你不希望每次搜索时都要遍历整个数组。
在这种情况下,您可以创建一个表示该数组索引的关联数组(哈希表或字典)。也就是说,它将每个数组元素映射到它在数组中的索引:
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"