在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"

其他回答

考虑到:

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的兼容性)

我有这样的情况,我必须检查一个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")类型的数组,而是用于换行分隔的脚本输出。


附言:不能评论一个答案,因为我是相对较新的…

结合这里介绍的一些想法,您可以创建一个没有循环的优雅if语句,它可以精确地进行单词匹配。

find="myword"
array=(value1 value2 myword)
if [[ ! -z $(printf '%s\n' "${array[@]}" | grep -w $find) ]]; then
  echo "Array contains myword";
fi

这将不会触发一个单词或val,只有整个单词匹配。如果每个数组值包含多个单词,则会中断。

别胡闹了!使您的解决方案简单、干净和可重用。

这些函数负责索引数组和关联数组。可以通过将搜索算法从线性搜索升级为二进制搜索(用于大型数据集)来改进它们。

##
# Determines if a value exists in an array.
###
function hasArrayValue ()
{
    local -r needle="{$1:?}"
    local -nr haystack="{$2:?}"  # Where you pass by reference to get the entire array in one argument.

    # Linear search. Upgrade to binary search for large datasets.
    for value in "${haystack[@]}"; do
        if [[ "$value" == "$needle" ]]; then
            return 0
        fi
    done

    return 1
}

##
# Determines if a value exists in an associative array / map.
###
function hasMapValue ()
{
    local -r needle="{$1:?}"
    local -nr haystack="{$2:?}"

    # Linear search. Upgrade to binary search for large datasets.
    for value in "${haystack[@]}"; do
        if [[ $value == $needle ]]; then
            return 0
        fi
    done

    return 1
}

是的,同样的逻辑,但在处理bash时,如果函数的名称可以让您知道迭代的对象(或不迭代的对象),则可能(可能)有用。

containsElement () { for e in "${@:2}"; do [[ "$e" = "$1" ]] && return 0; done; return 1; }

现在正确处理空数组。