删除重复的行,同时保持行的顺序


14
[root@server]# awk '!seen[$0]++' out.txt > cleaned
awk: (FILENAME=out.txt FNR=8547098) fatal error: internal error
Aborted
[root@server]#

““服务器””具有:8 GB RAM + 16 GB SWAP,x> 300 GB可用空间,amd64,台式机CPU。科学Linux 6.6。没有其他东西可以使它加载。Awk在几秒钟后中止。out.txt为〜1.6 GB。GNU Awk 3.1.7。

问题:如何在保持行顺序的同时删除重复的行?大小写也很重要,例如:“ A”和“ a”是两个不同的行,必须保留它。但是“ a”和“ a”是重复的,只需要第一个。

答案可能在任何情况下..如果awk对此不利。.那么perl / sed ..可能是什么问题?

[root@server]# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 61945
max locked memory       (kbytes, -l) 99999999
max memory size         (kbytes, -m) unlimited
open files                      (-n) 999999
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 99999999
cpu time               (seconds, -t) unlimited
max user processes              (-u) 61945
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
[root@server]# 

更新:我在RHEL机器上尝试了此操作,它不会中止运行,但是我没有时间等待它完成。.为什么SL linux与RHEL不同?

更新:我正在尝试在Ubuntu 14虚拟gues ..到目前为止,它可以工作!这不是一个无限的问题:mawk 1.3.3

root@asdf-VirtualBox:~# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 51331
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 51331
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
root@asdf-VirtualBox:~# 

2
您的示例中没有重复的行...?
mikeserv

1
awk两台机器的版本是什么?
cuonglm

最新的rhel和最新的sl linux,不知道rhel的版本
。sl

有多大out.txt?如果在较小的文件上尝试使用相同的命令,是否可以使用?机器上有多少用户?是否有足够的可用内存用于该过程?输入文件的8547098行是否有特殊之处?
terdon

Answers:


22

我怀疑这会有所不同,但是以防万一,这是在Perl中执行相同操作的方法:

perl -ne 'print if ++$k{$_}==1' out.txt

如果问题是将唯一的行保留在内存中,那将与awk您尝试的问题相同。因此,另一种方法可能是:

cat -n out.txt | sort -k2 -k1n  | uniq -f1 | sort -nk1,1 | cut -f2-

怎么运行的:

  1. 在GNU系统上,cat -n将在每行的行号前添加一定数量的空格,后跟<tab>字符。cat通过管道将此输入表示形式传递到sort

  2. sort-k2选项指示它在排序时仅考虑从第二个字段到行尾的字符,并sort默认在空白(或cat插入的空格和<tab>上分割字段。
    当紧随其后时-k1n,首先sort考虑第二个字段,然后考虑第二个字段(在相同-k2字段的情况下),它考虑第一个字段,但按数字排序。因此,重复的行将按照出现的顺序进行排序。

  3. 结果将通过管道传递到uniq-被告知忽略第一个字段-f1也被空格隔开),并在原始文件中产生唯一行的列表,然后通过管道传递回sort
  4. 这次sort以数字方式对第一个字段cat插入的行号)进行排序,使排序顺序恢复为原始文件中的顺序,并将这些结果通过管道传递给cut
  5. 最后,cut删除由插入的行号cat。这cut仅通过从第二个字段到行尾的打印来实现cut默认分隔符是<tab>字符)

为了显示:

$ cat file
bb
aa
bb
dd
cc
dd
aa
bb
cc
$ cat -n file | sort -k2 | uniq -f1 | sort -k1 | cut -f2-
bb
aa    
dd
cc

嗨,Terdon,OP需要保持行顺序,因此cat | sort | uniq方法将不起作用...就像您的perl版本一样……
Lambert 2015年

1
不错的解决方案sort!但是大多数人sort都可以自己做uniq,所以您可以通过sort -uk2 | sort -bk1,1n
Costas

@Costas最sort吗?我以为-u是GNU功能。
terdon

@don_crissti啊,就是这样,谢谢。我怎么在这里使用呢?正如我刚刚注意到的(并进行修改以进行修复),我需要先在第二个字段上排序,然后再在第一个字段上按数字排序,以保持行顺序。然后,如何使用-u并指定应忽略第一个字段?根据man sort,这-u不是的可能选项之一-f,因此我认为此处不能使用它。
terdon

1
这就是施瓦兹变换!(+1)
JJoao 2015年

7
#!/usr/bin/perl 
use DB_File;
tie %h, 'DB_File';

while(<>){ not $h{$_} and print and $h{$_}=1 }

编辑1:真的有效吗?(比较中)

Sol1 : Terdon et all Schwartzian-transform-like one-liner
    cat -n _1 | sort -uk2 | sort -nk1 | cut -f2-

Sol2 : perl  + DB_File (this answer)
    perl dbfile-uniq _1

Sol3 : PO (John W. Gill solution has a similar behavior)
    awk '!seen[$0]++' _1

Sol4: Terdon perl
    perl -ne 'print if ++$k{$_}==1' _1

情况1:100_000_000个随机数(每个5位数),566Mbytes,31_212个不同的值:

$ while true ; do echo $RANDOM; done | head -100000000 > _1

情况2:50_000_000兰德编号(每个10位数字),516 MB,48_351_464个不同的值:

$ shuf _1 |  sed 'N;s/\n/ /' > _11

(以下数字不是很准确):

┌────────┬────────┬────────────────┬────────┬──────┐
         Sol1    Sol2            Sol3    Sol4 
         sort...│ perl DB         awk     perl 
├────────┼────────┼────────────────┼────────┼──────┤
 case 1  6m15    6m17            0m28    0m28 
├────────┼────────┼────────────────┼────────┴──────┤
 case 2  11m15   81m44           out of memory 
├────────┼────────┼────────────────┼────────┬──────┤
 case 2          5m54 /cache=2G               
└────────┴────────┴────────────────┴────────┴──────┘

带有缓存的sol2是:

use DB_File;
use Fcntl ;

$DB_HASH->{'cachesize'} = 2000_000_000;
tie %h, 'DB_File', "_my.db", O_RDWR|O_CREAT|O_TRUNC, 0640, $DB_HASH;

while(<>){ not $h{$_} and print and $h{$_}=1 }

还可以通过添加cachesize选项来优化排序(未完成)。

一个简单的结论:

  • sort 是一个很棒的命令!

1
sort -uk2并且sort -nk1,1是不同的。第一个考虑从2cd键到行尾,第二个考虑第一个键。您应该在sort -nk1那里更改-这样可能会更快,但绝对会更可靠。顺便说一句-这些是一些漂亮的盒子。
mikeserv

@mikeserv,谢谢您的评论。由于K1,1是唯一的,因此对-nk1和sort -nk1,1进行排序会返回某些结果。我都尝试过,结果是一样的,时间也不是唯一的。
JJoao 2015年

这很有道理-不过,感谢您的尝试。所以cat -n做了标签?我不知道该命令的工作方式。
mikeserv

1
@mikeserv,愉快地将cat -n它们转换linespaces + the number + \t + line-
JJoao 2015年

1

我用过

awk -v BINMODE=rw '!($0 in a){a[$0];print}' infile >> outfile

BINMODE = rw:使行结束符保持高兴。(我住在混合操作系统环境中)

逻辑很简单。

如果当前行不在关联数组中,则将其添加到关联数组并打印以输出。

这种方法可能存在内存限制。对于非常大的文件和文件集,我使用了一些变体,使用文件存储来克服限制。


0

问题的顺序保留语义具有出色的属性:您可以细分问题。你可以做split -l 1000000在输入文件上执行;它产生的1000000行代码具有按词法排列的名称,这很好。然后统一碎片;然后(作为第二遍)统一这些输出。

这解决了内存不足的问题(通过限制内存需求),但以将其转变为多通道解决方案为代价。

特别:

生成输入数据:

$ cat make-uniqm-input.py
#!/usr/bin/env python
import random
n = 1000000
for i in xrange(0, n):
    print random.randint(1000, 2000)

$ python make-uniqm-input.py  > uniqm-input.txt

$ wc -l uniqm-input.txt
 1000000 uniqm-input.txt

拆分输入数据:

$ split -l 10000 uniqm-input.txt

$ ls x?? | head
xaa
xab
xac
xad
xae
xaf
xag
xah
xai
xaj

$ ls x?? | wc -l
     100

$ cat x?? | wc -l
 1000000

一次运行uniqifier(将所有唯一的输入行保留在内存中):

# 'uniqm' is any order-preserving uniq implementation, such as
# gawk '!counts[$0]++'.
$ uniqm < uniqm-input.txt > output-no-splitting.txt

$ wc -l output-no-splitting.txt
    1001 output-no-splitting.txt

在拆分的片段上运行uniqifier(仅在内存中保留每个片段的唯一输入行),然后减少第二遍:

$ for x in x??; do uniqm < $x; done | uniqm > output-with-splitting.txt

$ wc -l output-with-splitting.txt
    1001 output-with-splitting.txt

比较:

$ diff output-no-splitting.txt output-with-splitting.txt

$ head uniqm-input.txt
1506
1054
1623
1002
1173
1400
1226
1340
1824
1091

$ head output-with-splitting.txt
1506
1054
1623
1002
1173
1400
1226
1340
1824
1091

我不知道输入中唯一行与非唯一行的比率,也不知道输入行的混合程度如何-因此,需要根据需要分割的文件数量进行一些调整。


0

另一种方法(值得发布作为单独的答案)是:代替创建临时文件的拆分文件方法,在uniqifier软件本身中进行批处理。例如,出于说明目的,使用Ruby uniqifier实现:

require 'set'
line_batch_count = 50000 # tunable parameter
lines_seen = Set.new
line_number = 0
ARGF.each do |line|
   line_number += 1
   if (line_number % line_batch_count) == 0
     lines_seen.clear
   end
   unless lines_seen.include? line
      puts line
      lines_seen << line
   end
end

这样做的想法是经常清除哈希集。然后,这变成了迭代:

$ cat uniqm-input.txt | ruby uniqm-capped.rb | wc -l
   20021

$ cat uniqm-input.txt | ruby uniqm-capped.rb | ruby uniqm-capped.rb | wc -l
    1001

$ cat uniqm-input.txt | ruby uniqm-capped.rb | ruby uniqm-capped.rb | head
1506
1054
1623
1002
1173
1400
1226
1340
1824
1091

因此,您可以重复运行此受限制的版本,直到行数在一次迭代和下一次迭代之间保持不变为止。

请注意,这种上限uniqm技术与语言无关:lines_seen无论您使用的是awk,python,perl,C ++等,您都可以每N行清除一次数组。我相信awkdelete是非标准的,但很常见。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.