Bash 检查数组元素是否包含在另一个数组中

6

我找到了一个很酷的Bash函数,可以检查数组是否包含元素:

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

以下是它的使用示例:
if CONTAINS_ELEMENT $element "${array[@]}"; then
... 
fi

我的问题是: 有没有一种方法可以重新编写这个函数,以便它可以检查一个数组中的任何值是否等于另一个数组中的任何值,而不仅仅是像当前这样检查单个值?


不正确!尝试使用 "${@:2}" - TrueY
非常类似于数组交集问题[1](https://dev59.com/a3E85IYBdhLWcg3wmExW)和[2](https://dev59.com/SGsz5IYBdhLWcg3wfnwx)。 - l0b0
2
为了显示问题已解决,请将其中一个答案标记为“已接受”。 - fedorqui
4个回答

3

已更正#3

尝试使用以下代码。函数ArrContains接受两个参数,即两个数组的名称。它从lArr1创建一个临时哈希表,然后检查lArr2中的任何元素是否在该哈希表中。这样就可以避免嵌套的for循环。

#!/usr/bin/bash

ArrContains(){
  local lArr1 lArr2
  declare -A tmp
  eval lArr1=("\"\${$1[@]}\"")
  eval lArr2=("\"\${$2[@]}\"")
  for i in "${lArr1[@]}";{ ((++tmp['$i']));}
  for i in "${lArr2[@]}";{ [ -n "${tmp[$i]}" ] && return 0;}
  return 1
}

arr1=("a b" b c)
arr2=(x 'b c' e)
arr3=(q a\ b y)
ArrContains arr1 arr2 && echo Contains arr1 arr2
ArrContains arr1 arr3 && echo Contains arr1 arr3

输出:

Contains arr1 arr3

另一种方法是定义某个分隔符并连接第一个哈希值。然后搜索匹配的SEPitemSEP字符串。
ArrContainsRe(){
  local lArr1 lArr2 tmp
  eval lArr1=("\"\${$1[@]}\"")
  printf -v tmp ",%s" "${lArr1[@]}";
  tmp="$tmp,"
  eval lArr2=("\"\${$2[@]}\"")
  for i in "${lArr2[@]}";{ [[ $tmp =~ ,$i, ]] && return 0;}
  return 1
}
...
ArrContainsRe arr1 arr2 && echo ContainsRe arr1 arr2
ArrContainsRe arr1 arr3 && echo ContainsRe arr1 arr3

输出:

ContainsRe arr1 arr3

@l0b0:你说得对!第一个函数处理得不好。我已经纠正了。谢谢! - TrueY
@l0b0:我刚想给你的最后一条评论点赞,但它被删除了... ;) 无论如何,还是谢谢你! - TrueY
当我尝试这个时,它失败了。无论如何它都没有返回任何东西。 - Dan-Simon Myrland
@Dan-SimonMyrland:抱歉,我在上次的更正中反应过度了!已经删除了错误的单引号。已更正!谢谢! - TrueY
@TrueY 而且您的输入仍然可能包含该字符。关键是,没有任何字符(或字符序列)可用作分隔符,这些字符不能成为输入的一部分。 - l0b0
显示剩余5条评论

1
循环内部的循环:
#!/bin/bash
clear

arrA=("a" "b" "c" "d" "e" "f")
arrZERO=("c" "e") # must be turned to "0"

echo "arrA:"
echo ${arrA[@]}
echo ""
echo "arrZERO:"
echo ${arrZERO[@]}

for (( i=0; i < ${#arrA[@]}; i++ ))
do
    for (( j=0; j < ${#arrZERO[@]}; j++ ))
    do
        if [[ ${arrA[$i]} = ${arrZERO[$j]} ]]; then
            arrA[$i]="0"
        fi
    done
done

echo ""
echo "FINAL"
echo ${arrA[@]}

终端将显示:
arrA:
a b c d e f

arrZERO:
c e

FINAL
a b 0 d 0 f

0

看了上面的链接,我找到了一个几乎能够满足我的需求的解决方案,但还不太完美:

parameters=($1 $2 $3 $4)
arg1=(h help -h --help)

PARAMETERS(){
pr1=" $parameters[@]} "
for item in ${@:1}; do
  if [[ $pr1 =~ " $item " ]]; then
    return 0
  else
    return 1
  fi
done
}

if PARAMETERS "${arg1[@]}"; then
  echo "It works!"
else
  echo "Nope, still not working..."
fi

现在这段代码在传递参数“h”时可以工作,但是在传递数组中的其他参数(-h --help help)时无法工作。如果我在这里犯了一些愚蠢的错误,请原谅,当涉及到bash脚本编写时,我有点新手 :)


一个语法问题:pr1=" $parameters[@]} " 可能应该是 pr1=" ${parameters[@]} "。但我建议使用 local pr1=...。还有一个语义问题:在 PARAMETERS 的第一次循环中,if 将返回。我认为 else 部分是不需要的。 - TrueY
好的,你自己的解决方案证明了解决我的问题的更好方法。再次感谢你的帮助! - Dan-Simon Myrland

-1

这应该可以:

any_overlap() {
    for e1 in "${array1[@]}"
    do
        for e2 in "${array2[@]}"
        do
            if [[ "$e1" = "$e2" ]]
            then
                return 0
            fi
        done
    done
    return 1
}

测试会话:

$ array1=(foo bar baz) array2=(ban bat bar) && any_overlap; echo $?
0
$ array1=(foo bar baz) array2=(ban bat) && any_overlap; echo $?
1
$ array1=() array2=() && any_overlap; echo $?
1

当然,如果数组已排序或数组元素中没有包含空格,则有更快的方法来完成它。


你如何将两个数组作为参数传递?这是不可能的。 - l0b0
你能否在这种情况下删除评论?现在它只是噪音 :/ - l0b0
@choroba:我认为在 bash 中使用隐式参数是可以的。 - TrueY
@TrueY:我并没有说这样不可以。只是与原始函数相比,它有不同的行为。 - choroba
1
@choroba:无法实现完全相同。必须添加一些技巧,因为“bash”无法将两个单独的数组传递给函数。 - TrueY
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接