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

删除重复行,同时保持行的顺序
[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 虚拟机..到目前为止它有效!这不是 ulimit 问题:莫克 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:~# 

答案1

我怀疑这会有所不同,但为了以防万一,以下是如何在 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将在每行前面添加行号,后面跟随一些空格,然后是<选项卡>特点。cat将此输入表示通过管道传输到sort.

  2. sort-k2选项指示它在排序时仅考虑第二个字段到行尾的字符,并sort默认在空格上拆分字段(或cat的插入空格和<选项卡>
    当后跟 时-k1nsort首先考虑第二个字段,然后再考虑(在相同-k2字段的情况下)它考虑第一个字段,但按数字排序。因此,重复的行将按它们出现的顺序排序在一起。

  3. 结果通过管道传输到uniq- 被告知忽略第一个字段-f1- 也可以用空格分隔)- 这会产生原始文件中唯一行的列表,并通过管道返回到sort.
  4. 这次sort在第一个字段上排序(cat的插入行号)以数字方式,将排序顺序恢复为原始文件中的顺序,并将这些结果通过管道传输到cut.
  5. 最后,cut删除 插入的行号cat。这是通过cut仅从第二个字段打印到行尾来实现的(并且cut的默认分隔符是<选项卡>特点)

为了显示:

$ 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

答案2

#!/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 位),516Mbytes,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 }

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

一个简单的结论:

  • sort这是一个很棒的命令!

答案3

我用过

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

BINMODE=rw :保持行尾终止符快乐。 (我生活在混合操作系统环境中)

逻辑很简单。

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

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

答案4

另一种方法(值得作为单独的答案发布)是:不要使用创建临时文件的分割文件方法,而是在 uniqifier 软件本身内进行批处理。例如,使用 Ruby 唯一标识符实现来进行说明:

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

因此,您可以重复运行此上限版本,直到行数从一次迭代到下一次迭代都不会改变。

请注意,这种 capped-uniqm 技术与语言无关:lines_seen无论您使用的是 awk、python、perl、C++ 等,您都可以每 N 行清除一次数组。所有这些语言都有 set-clear 方法;我相信awk'sdelete是非标准但常见的。

相关内容