使用 awk 删除重复项非常常见且简单。但当我们只比较一列时,我只需要打印那些重复的行。我尝试了这个命令:
awk 'seen[$2]++'
但正如你所看到的,它有缺陷。它会打印重复项,但仅限于它们第二次出现后。我才刚刚开始习惯 unix 和 bash,所以如果你能向我解释解决方案那就太好了。
答案1
我可以看到有两种方法可以做到这一点:
迭代文件两次:
在第一次迭代中,计算每个 $2 出现的次数。
在第二次迭代中,仅打印计数大于 1 的行awk 'NR == FNR {count[$2]++; next} count[$2] > 1' file file
数据的单次迭代:
你需要统计每个$2出现的次数,和记住每 2 美元发生了哪些行。
这个答案使用 GNU awk 来表示数组的数组。输出的顺序不可能与输入数据相同。它还必须将整个文件存储在内存中。
gawk ' { lines[$2][++count[$2]] = $0 } END { for (x in lines) if (count[x] > 1) for (i=1; i<=count[x]; i++) print lines[x][i] } ' file
使用输入文件进行测试:
$ cat file
a b
b b
c b
a c
a d
b d
a e
和预期产出
a b
b b
c b
a d
b d
答案2
使用相同的样本输入格伦·杰克曼的回答
$ awk '$2 in seen{if(c[$2]--){print fl[$2]} print} !seen[$2]++{fl[$2]=$0; c[$2]=1}' file
a b
b b
c b
a d
b d
!seen[$2]++
如果$2
之前没有遇到过:fl[$2]=$0
保存第一行,我假设输入未排序并且重复项可能出现在文件中的任何位置,因此基于$2
而不是仅临时变量保存它c[$2]=1
类似地,将计数变量初始化为 1
$2 in seen
如果$2
之前发生过:if(c[$2]--){print fl[$2]}
首先打印上一行,计数器递减,以便后续匹配的条件将失败print
然后打印当前行
与一些其他输入
$ cat ip.txt
6.2 : 897 : bar
3.1 : 32 : foo
1.2 : 123 : xyz
2.3 : 32 : baz
7.5 : 897 : boo
$ awk -F: '$2 in seen{if(c[$2]--){print fl[$2]} print} !seen[$2]++{fl[$2]=$0; c[$2]=1}' ip.txt
3.1 : 32 : foo
2.3 : 32 : baz
6.2 : 897 : bar
7.5 : 897 : boo
请注意,顺序取决于重复发生的方式
答案3
当您迭代同一个文件两次时,您可以使用行号作为方便的索引;它可以使逻辑更清晰。
awk 'NR == FNR {if ($2 in z) { y[z[$2]]; y[FNR] } z[$2]=FNR; next} (FNR in y)' file file
我在回答这个问题时使用了类似的技巧:
这个技巧的基础是 Awk 将简单地通过引用来创建一个变量,并且该index in arrayname
构造根据是否已使用指定索引创建了数组元素来返回 true 或 false。