0

我有一个包含重复记录的文件(重复记录在列中)。我只想保留文件中最后一次出现的 dup 记录,并将所有其他 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在输出文件中

4

3 回答 3

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

在此处验证http://ideone.com/IBrNeh

在我的本地机器上使用该文件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
于 2013-03-15T21:03:09.493 回答
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
于 2013-03-15T21:43:09.743 回答
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 的版本和我的版本进行了测试,但我无法在我的系统上重现这些差异。

更新:我使用 Sudo_O 的版本cat而不是tac. 尽管在一个系统上,输出到 /dev/null 之间和输出到 /dev/nulltac之间有 0.2 秒的差异(参见这篇文章的底部)taccat

我有:

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
于 2013-03-15T23:26:59.200 回答