【问题标题】:Check if an element is present in a Bash array [duplicate]检查Bash数组中是否存在元素[重复]
【发布时间】:2012-12-31 05:49:57
【问题描述】:

我想知道是否有一种有效的方法来检查一个元素是否存在于 Bash 的数组中?我正在寻找类似于我可以在 Python 中执行的操作,例如:

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

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

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

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

【问题讨论】:

标签: arrays bash shell search


【解决方案1】:

抛开明显的警告,如果你的数组真的像上面的那样,你可以这样做

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

【讨论】:

  • +1 用于教授新事物。但是d 也会匹配xdy
  • @OlafDietsche 这可以通过写if [[ ${arr[*]} =~ $(echo '\<d\>') ]] (source)来解决
  • 感谢您的帮助。但是,如果我没有 else 追随者,我怎么能否定结果呢?这将节省一些冗余代码。 @Steven Penny
  • @SvenM。如果 [[ ! ${arr[*]} =~ d ]] ?
【解决方案2】:

你可以这样做:

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 Conditional Expressions

【讨论】:

  • 此代码是否适用于数字和字符串值?例如,如果我的数组同时包含字符串和数组,我可以同时搜索吗?
  • 是的。 Bash 将毫无困难地将数值作为字符串处理
  • 移除位置参数的第一个元素。
  • 在我的实现中,我必须将 "${!array}" 更改为 "${array[@]}" 才能工作。我认为“array_contains”和“array_contains2”提案更安全,因为许多使用 grep 的人返回误报。
  • 考虑引用$seeking,这样它就不会被视为一种模式。
【解决方案3】:

就计算时间而言,这是另一种可能比迭代更快的方法。不确定。思路是将数组转换为字符串,截断,得到新数组的大小。

例如,查找'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`

【讨论】:

    【解决方案4】:

    FWIW,这是我使用的:

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

    这适用于任何数组项或搜索目标中没有分隔符的情况。我不需要解决我的应用程序的一般情况。

    【讨论】:

      【解决方案5】:

      如果数组元素不包含空格,另一种(可能更具可读性)解决方案是:

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

      【讨论】:

      • +1。不错的答案。此解决方案适用于我检查数字数组中是否存在数字。在我的情况下,@Steven Penny 的解决方案对于数字数组并不是 100% 正确的。如果回显 ${combined_p2_idx[@]} | grep -q -w $subset_id;那么
      • 我同意@GoodWill。这应该是公认的答案。其他选项没有那么可靠。
      • 如果数组中有负数会出错
      • 添加 -- 表示参数结束,负数不会被视为 grep 的参数
      • 到目前为止,最优雅和易于理解的解决方案。对我来说就像一个魅力。
      【解决方案6】:
      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 结果添加 || &lt;run_your_if_notfound_command_here&gt;

      【讨论】:

      • @Querty 我不知道为什么这不是最佳答案。 grep -x... 使其使用起来最简单。
      【解决方案7】:

      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
      

      【讨论】:

      • 这是线程中唯一对我有用的回复,谢谢!
      • 这非常简洁,我可以将它与BASH_ARGV 一起使用来测试是否给出了某个单词作为需要不同一次性设置的参数,而无需执行某种操作argparse 在这种情况下是多余的。
      • 这是我发现整个 StackOverflow 工作的唯一答案。
      【解决方案8】:

      由于 bash 没有内置 value in array 运算符和 =~ 运算符或 [[ "${array[@]" == *"${item}"* ]] 符号让我感到困惑,我通常将grep 与此处的字符串组合:

      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 以匹配整行。
      猜你喜欢
      • 2011-11-14
      • 2015-10-06
      • 2018-02-24
      • 2011-06-15
      • 1970-01-01
      • 2011-06-03
      相关资源
      最近更新 更多