使用Shell脚本比较两个不同的数组

3

我们如何在Shell脚本中比较两个数组并显示结果?

假设我们有以下两个数组:

list1=( 10 20 30 40 50 60 90 100 101 102 103 104)
list2=( 10 20 30 40 50 60 70 80 90 100 )

我的要求是按顺序比较这两个数组,仅显示来自list1的结果为(101 102 103 104)。 它不应包括在list2中存在但list1中不存在的值7080
这并没有帮助,因为它包括了所有内容:
echo "${list1[@]}" "${list2[@]}" | tr ' ' '\n' | sort | uniq -u

我尝试过类似下面这样的操作,但为什么它不起作用?
list1=( 10 20 30 40 50 60 70 90 100 101 102 103 104)
list2=( 10 20 30 40 50 60 70 80 90 100 )

for (( i=0; i<${#list1[@]}; i++ )); do
for (( j=0; j<${#list2[@]}; j++ )); do
     if [[ ${list1[@]} == ${list2[@] ]]; then
         echo 0
         break
             if [[  ${#list2[@]} == ${#list1[@]-1} && ${list1[@]} != ${list2[@]} ]];then
             echo ${list3[$i]}
         fi
     fi
done
done
3个回答

3
你可以使用comm来实现这个功能:
readarray -t unique < <(
    comm -23 \
        <(printf '%s\n' "${list1[@]}" | sort) \
        <(printf '%s\n' "${list2[@]}" | sort)
)

导致
$ declare -p unique
declare -a unique=([0]="101" [1]="102" [2]="103" [3]="104")

或者,为了获得您想要的格式,
$ printf '(%s)\n' "${unique[*]}"
(101 102 103 104)

comm -23 接受两个已排序的文件(在这里使用sort),并打印出第一个文件中独有的每一行;进程替代用于将列表输入到comm中。

然后,readarray 读取输出,并将每一行放入unique数组的一个元素中。(请注意,这需要Bash。)


你的尝试失败了,其中一个原因是你试图在单个比较中比较多个元素。
[[ ${list1[@]} != ${list2[@]} ]]

扩展为

[[ 10 20 30 40 50 60 90 100 101 102 103 104 != 10 20 30 40 50 60 70 80 90 100 ]]

Bash抱怨说,期望一个二进制运算符而不是第二个元素,20

0

关联数组对此非常方便:

list1=( 10 20 30 40 50 60 90 100 101 102 103 104)
list2=( 10 20 30 40 50 60 70 80 90 100 )
typeset -a onlyList1
typeset -A inList2
for elem in "${list2[@]}"; do inList2["$elem"]=1; done
for elem in "${list1[@]}"; do [[ -v inList2["$elem"] ]] || onlyList1+=("$elem"); done
typeset -p onlyList1

typeset -a onlyList1=(101 102 103 104)

或者类似地,从list1开始,并删除list2中的内容:
typeset -A inList1
for elem in "${list1[@]}"; do inList1["$elem"]=1; done
for elem in "${list2[@]}"; do unset inList1["$elem"]; done
onlyList1=( "${!inList1[@]}" )

感谢您的输入,但是在我的环境中这个方法不起作用。 - DBA

0

也可以使用这种方法

#!/bin/ksh

list1=( 10 20 30 40 50 60 90 100 101 102 103 104 )
list2=( 10 20 30 40 50 60 70 80 90 100 )

# Creating a temp array with index being the same as the values in list1

for i in ${list1[*]}; do
        list3[$i]=$i
done

# If value of list2 can be found in list3 forget this value

for j in ${list2[*]}; do
        if [[ $j -eq ${list3[$j]} ]]; then
                unset list3[$j]
        fi
done

# Print the remaining values

print ${list3[*]}

输出为

101 102 103 104

希望能有所帮助
编辑
如果两个列表相同的情况下:
# Print the remaining values

if [[ ${#list3[*]} -eq 0 ]]; then
        print "No differences between the list"
else
        print ${list3[*]}
fi

谢谢。这对我的目的非常有效。但是当我尝试处理异常时,它的响应不好。 如果[[ -n ${list3} ]]; then real_val=$(echo 0) else real_val=$(echo "${list3[@]}") fiecho $real_val - DBA
什么类型的异常? - Andre Gelinas
-z太不起作用了。在if子句中尝试过list3[@]和list3[*],都没有成功。有什么建议可以解决这个问题吗? - DBA
假设list1和list2两个数组中包含相同的值,那么数组的输出将为null。所以,我想要做的是,如果数组返回null,则显示0。 - DBA
谢谢再次。它正常工作了。看起来算术比较得到了实际结果。 - DBA

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