命令行"sort | uniq -c | sort -n"的替代方法



我使用sort | uniq -c | sort -n多年,但今天它失败了,因为我的输入文件是 10 GB,而我的/tmp是 1 GB 宽:

sort: write failed: /tmp/sortmIGbL: No space left on device

因此,我正在寻找一种日常使用的有效替代方案:

  • 可以使用awk,但没有排序的关联数组

  • perl似乎是一个不错的选择,但 perlmonks.org 的 10 年前的解决方案似乎不起作用

    no warnings;
    $^W=0;
    open my $in, $ARGV[0] or die "Couldn't open $ARGV[0]:$!";
    my ($buffer, %h) = ''; keys %h = 1024*500;
    while (sysread($in, $buffer, 16384, length $buffer)) {
    $h{$1}++ while $buffer =~ m[^(?:.+?|){9}([^|]+)|]mg;
    $buffer = substr($buffer, rindex($buffer, "n"));
    }
    print scalar keys %h;
    

如何在非常大的文件上获得与sort | uniq -c | sort -nr | head相同的结果?

  • 当我使用 Linux/Cygwin/Solaris/*BSD/...我对任何想法都持开放态度(便携式与否)
  • 您可以自由使用所需的脚本语言(awk/perl/...)

输入示例

a
BB
ccccc
dddddddd
a
BB
a

可能的输出之一

3 a
2 BB
1 dddddddd
1 ccccc

命令链中的第一个sort是使用所有资源的命令。通过首先获取唯一行,然后排序来减少问题集:

perl -ne '
$count{$_}++;
END {
print "$count{$_} $_" for sort {
$count{$b} <=> $count{$a} || $b cmp $a
} keys %count
}
' input.txt

您有 66,000 行7 字节的唯一行,因此哈希键占用的内存将为 66,000 * 56 字节(每个标量 = 键的 3,696,000 字节)。这不包括哈希的计数和开销,但毫无疑问,这种方法很容易解决问题。

排序不是顺序操作,例如,您不能只读取 10 条记录,对它们进行排序、转发,然后执行接下来的 10 条记录。因此,如果您想对 10GB 的数据进行排序,您可以

  • 需要大量内存,例如超过 10GB
  • 需要大量磁盘空间(至少 10GB)或就地排序,例如在文件内部(这将适用于固定大小的记录,但对于可变大小的记录来说将是一场噩梦)
  • 需要一种更智能的方法来解决您的问题(例如,如果记录大小为 1MB,但其中只有 10 个字节与排序相关,则使用智能算法可以更快并使用更少的内存)

顺便说一句,您是否尝试设置 TMPDIR,以便排序不使用/tmp,而是使用/var/tmp 或任何其他具有更多磁盘空间的目录?或者,也许您的排序有一个 -T 选项来指定临时目录。

使用 GNU awk 进行排序关联数组:

$ gawk '
BEGIN{ PROCINFO["sorted_in"] = "@val_num_desc" }
{ a[$0]++ }
END { for (i in a) print a[i], i }
' file
3 a
2 BB
1 dddddddd
1 ccccc

不知道它是否足以有效地处理您的大型数据集,只需按照他问题下方的 OP 注释中的要求显示一个 awk 排序的关联数组。

最新更新