在数组中查找重复元素

4
这个可以工作:
arr[0]="XX1 1"
arr[1]="XX2 2" 
arr[2]="XX3 3"
arr[3]="XX4 4"
arr[4]="XX5 5"
arr[5]="XX1 1"
arr[6]="XX7 7"
arr[7]="XX8 8"

duplicate() { printf '%s\n' "${arr[@]}" | sort -cu |& awk -F: '{ print $5 }'; }

duplicate_match=$(duplicate)

echo "array: ${arr[@]}"

# echo "duplicate: $duplicate_match"

[[ ! $duplicate_match ]] || { echo "Found duplicate:$duplicate_match"; exit 0; }

echo "no duplicate"

在相同的代码下,这个似乎不起作用,为什么?
arr[0]="XX"
arr[1]="wXyz" 
arr[2]="ABC"
arr[3]="XX"

你的代码实际上并没有起作用,因为当输入没有排序时,sort -cu 失败了。它在第一个数据集中发现的重复项只是按顺序排列后第一个出现的项目。 - chepner
管道符和商楠符组合只适用于c-shell,不适用于bash。 - thom
@chepner 谢谢,我会搜索如何在正确位置对数组进行排序。 - user3353499
@thom |& 也在bash的4版本中被添加了。 - chepner
@chepner 谢谢,我错了。管道符号和&确实是有效的。 - thom
2个回答

5
为了检查重复项,这段代码更简单且适用于两种情况:
uniqueNum=$(printf '%s\n' "${arr[@]}"|awk '!($0 in seen){seen[$0];c++} END {print c}')

(( uniqueNum != ${#arr[@]} )) && echo "Found duplicates"

编辑:要打印重复项,请使用以下awk命令:

printf '%s\n' "${arr[@]}"|awk '!($0 in seen){seen[$0];next} 1'

Awk命令使用一个名为seen的数组存储如果一行不是已经存在于seen数组中,则将其添加到该数组中并移至下一行。最后的1仅打印那些重复的行。


谢谢Anubhava,我需要学习你的代码才能充分理解它,请问如何将重复元素返回到echo中?另外,有人能纠正我的代码吗?我已经在这上面花费了两个小时,用另一个代码而不理解自己的代码很令人沮丧:( - user3353499
如果你想理解为什么你的代码失败了,可以参考chepner的答案。 - anubhava
我还在我的回答中添加了一些解释。 - anubhava
@Neeraj:尝试这个:printf '%s\n' "${arr[@]}" | awk '!seen[$0]++ {} END {print length(seen)} - anubhava

0
这里提供一个稍微有些愚蠢的解决方案。我只是想看看是否可以在不使用显式管道的情况下用单个命令完成此操作。(我认为对于非常大的数组/数组元素,显式管道可能更有效。)
请注意,这是测试重复数组元素的存在性,并且不输出重复项本身,尽管单独使用 awk 命令可以实现这一点。还要注意,如果您不幸拥有包含空格的数组元素,则以下内容将无法按描述进行评估。
[[ $( awk -v RS=" " ' a[$0]++ ' <<< "${arr[@]} " ) ]] && echo "dups found"

解释:

awk -v RS=" "

  • 对每个以空格为记录分隔符的输入记录执行后续的awk命令。基本上,这将使awk将每个数组元素视为单独的“行”。

' a[$0]++ '

  • awk命令有两个作用:

    • 返回数组a中键$0的值。如果该值大于0,则打印该行。与awk ' { $1=$2 } 1 '进行比较。

    • 将数组a中键$0的值加1。

<<< "${arr[@]} "

  • 作为awk命令的输入,请使用在打印arr中的每个元素时创建的字符串作为单独的单词,即用空格分隔加上额外的一个空格在末尾

  • }"之间的空格实际上非常重要,因为如果没有它,最后一个数组元素将没有空格,并且因此不会被awk视为不同的“记录”。

[[ $( ... ) ]]

  • 如果包含的awk命令有任何输出,则测试评估为0,即TRUE。

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