检查Bash数组中是否存在元素[重复]


75

我想知道是否有一种有效的方法来检查Bash数组中是否存在元素?我正在寻找类似于Python的功能:

arr = ['a','b','c','d']

if 'd' in arr:
    do your thing
else:
    do something

我已经看到了针对Bash 4+使用针对bash的关联数组的解决方案,但是我想知道是否还有其他解决方案。

请理解,我知道微不足道的解决方案是在数组中进行迭代,但是我不希望这样。


4
不要将“简洁”与“高效”混为一谈。但是,没有,没有简单的方法bash可以通过简单的数组来完成所需的操作。
chepner 2013年

Answers:


105

您可以这样做:

if [[ " ${arr[*]} " == *" d "* ]]; then
    echo "arr contains d"
fi

例如,如果您查找“ a b”,这将产生误报-该子字符串在连接的字符串中,但不作为数组元素。无论您选择哪种定界符,都会出现此难题。

最安全的方法是遍历数组直到找到元素:

array_contains () {
    local seeking=$1; shift
    local in=1
    for element; do
        if [[ $element == "$seeking" ]]; then
            in=0
            break
        fi
    done
    return $in
}

arr=(a b c "d e" f g)
array_contains "a b" "${arr[@]}" && echo yes || echo no    # no
array_contains "d e" "${arr[@]}" && echo yes || echo no    # yes

这是一个“更干净”的版本,您只需传递数组名称,而不是传递其所有元素

array_contains2 () { 
    local array="$1[@]"
    local seeking=$2
    local in=1
    for element in "${!array}"; do
        if [[ $element == "$seeking" ]]; then
            in=0
            break
        fi
    done
    return $in
}

array_contains2 arr "a b"  && echo yes || echo no    # no
array_contains2 arr "d e"  && echo yes || echo no    # yes

对于关联数组,有测试一个非常整洁的方式,如果该数组包含一个给定的关键:在-v运营商

$ declare -A arr=( [foo]=bar [baz]=qux )
$ [[ -v arr[foo] ]] && echo yes || echo no
yes
$ [[ -v arr[bar] ]] && echo yes || echo no
no

请参见手册中的6.4 Bash条件表达式


此代码适用于数字值和字符串值吗?例如,如果我的数组同时包含字符串和数组,我可以同时搜索吗?
QuantumLicht

是。Bash可以毫无困难地将数字值当作字符串来处理
glenn jackman

删除位置参数的第一个元素。
glenn jackman 2014年

1
在我的实现中,我必须将“ $ {!array}”更改为“ $ {array [@]}”才能正常工作。我认为“ array_contains”和“ array_contains2”提议更安全,因为许多使用grep的人会返回误报。
爱德华多·卢西奥

1
请考虑引用,$seeking以免将其视为模式。
佩萨

35

除了明显的警告外,如果您的阵列实际上像上面的阵列,则可以

if [[ ${arr[*]} =~ d ]]
then
  do your thing
else
  do something
fi

4
+1教新事物。但是d也会匹配xdy
Olaf Dietsche

3
@OlafDietsche可以通过编写它来解决if [[ ${arr[*]} =~ $(echo '\<d\>') ]]来源
Stefan van den Akker,2014年

谢谢,这很有帮助。但是,如果没有其他关注者,我该如何否定结果呢?这样可以节省一些冗余代码。@Steven Penny
Sven M.

1
@SvenM。如果[[!$ {arr [*]} =〜d]]吗?
rwenz3l

22

1)初始化数组arr并添加元素

2)设置要搜索的变量 SEARCH_STRING

3)检查您的数组是否包含元素

arr=()
arr+=('a')
arr+=('b')
arr+=('c')

SEARCH_STRING='b'

if [[ " ${arr[*]} " == *"$SEARCH_STRING"* ]];
then
    echo "YES, your arr contains $SEARCH_STRING"
else
    echo "NO, your arr does not contain $SEARCH_STRING"
fi

3
这是该帖子对我有用的唯一回复,谢谢!
西沙尔饶

这非常简明扼要,我可以使用它BASH_ARGV来测试是否给出了某个单词作为参数,而该参数要求使用一次性的不同设置,而不必argparse为这种情况做过多的设置。
dragon788'6

16

如果数组元素不包含空格,则另一个解决方案(也许更易读)将是:

if echo ${arr[@]} | grep -q -w "d"; then 
    echo "is in array"
else 
    echo "is not in array"
fi

+1。好答案。此解决方案对我有用,可以检查数字数组中是否存在数字。@Steven Penny的解决方案在我的数字数组中无法100%正确地工作。如果echo $ {combined_p2_idx [@]} | grep -q -w $ subset_id; 然后
善意

我同意@GoodWill。这应该是公认的答案。其他选项没有那么强大。
Grr '17

如果数组中有负数,则会出错
jessi

添加--以表示参数的结尾,负数将不会被视为grep的参数
drAlberT

到目前为止,最优雅,最易懂的解决方案。对我来说就像一个魅力。
卢卡斯·利马

3
array=("word" "two words") # let's look for "two words"

使用grepprintf

(printf '%s\n' "${array[@]}" | grep -x -q "two words") && <run_your_if_found_command_here>

使用for

(for e in "${array[@]}"; do [[ "$e" == "two words" ]] && exit 0; done; exit 1) && <run_your_if_found_command_here>

对于not_found结果,请添加 || <run_your_if_notfound_command_here>


@Querty我不知道为什么这不是最好的答案。grep -x...使它最容易使用。
ldeck

3

由于bash没有内置的 in 数组运算符,并且该=~运算符或[[ "${array[@]" == *"${item}"* ]]符号使我感到困惑,因此我通常将其grep与here-string结合使用:

colors=('black' 'blue' 'light green')
if grep -q 'black' <<< "${colors[@]}"
then
    echo 'match'
fi

但是请注意,与完全包含要搜索的项目但不等于另一个项目时发生的许多其他答案一样,它也会遭受相同的误报问题:

if grep -q 'green' <<< "${colors[@]}"
then
    echo 'should not match, but does'
fi

如果这是用例的问题,则可能无法遍历数组:

for color in "${colors[@]}"
do
    if [ "${color}" = 'green' ]
    then
        echo "should not match and won't"
        break
    fi
done

for color in "${colors[@]}"
do
    if [ "${color}" = 'light green' ]
    then
        echo 'match'
        break
    fi
done

@sec作为上述@Querty的答案可以避免误报,所有需要做的就是添加-x到grep中以匹配整行。
ldeck

2

就计算时间而言,这是另一种比迭代更快的方法。不确定。这个想法是将数组转换为字符串,截断它,并获取新数组的大小。

例如,要查找索引“ d”:

arr=(a b c d)    
temp=`echo ${arr[@]}`
temp=( ${temp%%d*} )
index=${#temp[@]}

您可以将其转换为类似以下的功能:

get-index() {

    Item=$1
    Array="$2[@]"

    ArgArray=( ${!Array} )
    NewArray=( ${!Array%%${Item}*} )

    Index=${#NewArray[@]}

    [[ ${#ArgArray[@]} == ${#NewArray[@]} ]] && echo -1 || echo $Index

}

然后,您可以致电:

get-index d arr

它将回显3,可以分配给:

index=`get-index d arr`

0

FWIW,这是我使用的:

expr "${arr[*]}" : ".*\<$item\>"

这适用于任何数组项或搜索目标中没有定界符的情况。我不需要为我的申请解决一般情况。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.