![删除重复行,同时保持行的顺序](https://linux22.com/image/62608/%E5%88%A0%E9%99%A4%E9%87%8D%E5%A4%8D%E8%A1%8C%EF%BC%8C%E5%90%8C%E6%97%B6%E4%BF%9D%E6%8C%81%E8%A1%8C%E7%9A%84%E9%A1%BA%E5%BA%8F.png)
[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-
怎么运行的:
在 GNU 系统上,
cat -n
将在每行前面添加行号,后面跟随一些空格,然后是<选项卡>特点。cat
将此输入表示通过管道传输到sort
.sort
的-k2
选项指示它在排序时仅考虑第二个字段到行尾的字符,并sort
默认在空格上拆分字段(或cat
的插入空格和<选项卡>)。
当后跟 时-k1n
,sort
首先考虑第二个字段,然后再考虑(在相同-k2
字段的情况下)它考虑第一个字段,但按数字排序。因此,重复的行将按它们出现的顺序排序在一起。- 结果通过管道传输到
uniq
- 被告知忽略第一个字段(-f1
- 也可以用空格分隔)- 这会产生原始文件中唯一行的列表,并通过管道返回到sort
. - 这次
sort
在第一个字段上排序(cat
的插入行号)以数字方式,将排序顺序恢复为原始文件中的顺序,并将这些结果通过管道传输到cut
. - 最后,
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
是非标准但常见的。