我有一个带有重复记录的文件(重复在列中)。我想只保留文件中最后一次出现的dup记录,并将所有其他副本移到另一个文件中。
文件:输入
foo j
bar bn
bar b
bar bn
bar bn
bar bn
kkk hh
fjk ff
foo jj
xxx tt
kkk hh
我使用了以下awk语句来保留最后一次出现 -
awk '{line=$0; x[$1]=line;} END{ for (key in x) print x[key];}' input > output
文件:输出
foo jj
xxx tt
fjk ff
kkk hh
bar bn
如何将重复记录移动到另一个文件(留下最后一次出现)?
在一个文件中移动foo j
,请说d_output
并将foo jj
保留在输出文件
答案 0 :(得分:2)
一个技巧是使用tac
首先反转文件(比第一个更容易抓住第一个匹配):
$ tac file | awk 'a[$1]++{print $0 > "dup";next}{print $0 > "output"}'
$ cat output
kkk hh
xxx tt
foo jj
fjk ff
bar bn
$ cat dup
kkk hh
bar bn
bar bn
bar b
bar bn
foo j
编辑:
以下是目前3种解决方案超过100万行的基准数据:
sudo_o
real 0m2.156s
user 0m1.004s
sys 0m0.117s
kent
real 0m2.806s
user 0m2.718s
sys 0m0.080s
scrutinizer
real 0m4.033s
user 0m3.939s
sys 0m0.082s
在我的本地计算机上使用文件seq 1 1000000 > bench
:
# sudo_o
$ time tac bench | awk 'a[$1]++{print $0 > "dup";next}{print $0 > "output"}'
real 0m0.729s
user 0m0.668s
sys 0m0.101s
# scrutinizer
$ time awk 'NR==FNR{A[$1]=NR; next} A[$1]!=FNR{print>f; next}1' f=dups bench bench > output
real 0m1.093s
user 0m1.016s
sys 0m0.070s
# kent
$ time awk '$1 in a{print a[$1]>"dup.txt"}{a[$1]=$0}END{for(x in a)print a[x]}' bench > output
real 0m1.141s
user 0m1.055s
sys 0m0.080s
答案 1 :(得分:2)
tac
和rev
等工具很棒!但是,它们不是所有发行版的默认值,特别是我发现您已使用unix
标记了问题。另外tac
更改了output / dup.txt顺序,如果要保留订单,则需要额外的努力来维护订单。
试试这一行:
awk '$1 in a{print a[$1]>"dup.txt"}{a[$1]=$0}END{for(x in a)print a[x]}' file
以你的例子:
kent$ awk '$1 in a{print a[$1]>"dup.txt"}{a[$1]=$0}END{for(x in a)print a[x]}' file
foo jj
xxx tt
fjk ff
kkk hh
bar bn
kent$ cat dup.txt
bar bn
bar b
bar bn
bar bn
foo j
kkk hh
答案 2 :(得分:2)
您可以尝试另一个选项,通过两次读取输入文件来保持顺序:
awk 'NR==FNR{A[$1]=NR; next} A[$1]!=FNR{print>f; next}1' f=dups file file
输出:
bar bn
fjk ff
foo jj
xxx tt
kkk hh
重复:
$ cat dups
foo j
bar bn
bar b
bar bn
bar bn
kkk hh
@Sudo_O @WilliamPursell @ user2018441。 Sudo_O谢谢你的性能测试。我尝试在我的系统上重现它们,但它没有tac
可用,所以我测试了Kent的版本和我的版本,但我无法在我的系统上重现这些差异。
更新:我使用cat
代替tac
测试了Sudo_O的版本。虽然在tac
的系统上,当输出到/ dev / null时,tac
和cat
之间的差异为0.2秒(参见本文底部)
我得到了:
Sudo_O
$ time cat <(seq 1 1000000) | awk 'a[$1]++{print $0 > "/dev/null";next}{print $0 > "/dev/null"}'
real 0m1.491s
user 0m1.307s
sys 0m0.415s
kent
$ time awk '$1 in a{print a[$1]>"/dev/null"}{a[$1]=$0}END{for(x in a)print a[x]}' <(seq 1 1000000) > /dev/null
real 0m1.238s
user 0m1.421s
sys 0m0.038s
scrutinizer
$ time awk 'NR==FNR{A[$1]=NR; next} A[$1]!=FNR{print>f; next}1' f=/dev/null <(seq 1 1000000) <(seq 1 1000000) > /dev/null
real 0m1.422s
user 0m1.778s
sys 0m0.078s
-
当使用文件而不是我得到的seq
时:
Sudo_O
$ time cat <infile | awk 'a[$1]++{print $0 > "/dev/null";next}{print $0 > "/dev/null"}'
real 0m1.519s
user 0m1.148s
sys 0m0.372s
kent
$ time awk '$1 in a{print a[$1]>"/dev/null"}{a[$1]=$0}END{for(x in a)print a[x]}' <infile > /dev/null
real 0m1.267s
user 0m1.227s
sys 0m0.037s
scrutinizer
$ time awk 'NR==FNR{A[$1]=NR; next} A[$1]!=FNR{print>f; next}1' f=/dev/null <infile <infile > /dev/null
real 0m0.737s
user 0m0.707s
sys 0m0.025s
可能是由于缓存效应,对于较大的文件也会出现..创建infile:
$ time seq 1 1000000 > infile
real 0m0.224s
user 0m0.213s
sys 0m0.010s
在不同的系统上测试:
$ time cat <(seq 1 1000000) > /dev/null
real 0m0.764s
user 0m0.719s
sys 0m0.031s
$ time tac <(seq 1 1000000) > /dev/null
real 0m1.011s
user 0m0.820s
sys 0m0.082s