当 awk 太慢时,基于字段分割大文件的最佳方法

当 awk 太慢时,基于字段分割大文件的最佳方法

我在处理巨大的 .gz 文件(大于 500G)时遇到问题。我的目标是将这些文件中的每个字段按第四个字段分开。我之前用过一个漂亮的 awk 单行语句来做到这一点:

zcat file.txt.gz | awk 'NR>1{print >  $4}'

但不幸的是,这需要很长时间才能处理大文件,因此我尝试首先按大小拆分它们,然后在按字段拆分后连接每个文件。我可以使用以下方法拆分它们:

i=file.txt.gz
dir=$i
mkdir -p $dir
cd $dir
split -b 200M ../$i $i

for file in `ls *`; do zcat $file | awk 'NR>1{print >  $4}'; done

但是我如何通过第四个字段连接所有正确的文件呢?另外,真的没有更好的办法吗?当我使用 gz 文件分割时,我也遇到错误,就像这样说“文件意外结束”,所以我想我的分割也是错误的,但我不确定我是否朝着正确的方向前进,如果你有建议将会非常有帮助。

非常感谢你的帮助!弗拉

答案1

Satō Katsura 的文件描述符注释是正确的,假设有超过 1021 个(通常用户 FD 限制为 1024,stdin/stdout/stderr 为 -3)$4 的不同值您正在使用的gawk.

>当您使用或打印到文件时>>,文件将保持打开状态,直到出现显式close(),因此您的脚本正在累积 FD。自 Gawk v3.0 之前开始,FD 耗尽 ( ulimit -n) 的处理是透明的:遍历打开文件的链表,并且“暂时”关闭 LRU(最近最少使用)(从操作系统角度关闭以释放 FD) ,gawk在内部对其进行跟踪,以便稍后在需要时透明地重新打开)。您可以通过-W lint在调用时添加来看到这种情况的发生(从 v3.1 开始)。

我们可以像这样模拟问题(在bash):

printf "%s\n" {0..999}\ 2\ 3\ 0{0..9}{0..9}{0..9} | time gawk -f a.awk

这会生成 1,000,000 行输出,其中包含 1000 个唯一值 4 美元,在我的笔记本电脑上大约需要 17 秒。我的限制是 1024 个 FD。

 printf "%s\n" {0..499}\ 2\ 3\ {0..1}{0..9}{0..9}{0..9} | time gawk -f a.awk

这也会生成 1,000,000 行输出,但有 2000 个 $4 的唯一值,运行时间约为 110 秒(时间长了六倍多,并且有 100 万个额外的小页面错误)。

从跟踪 $4 的角度来看,上面是“最悲观”的输入,输出文件每一行都会更改(并保证每次都需要(重新)打开所需的输出文件)。

有两种方法可以帮助解决这个问题:减少文件名使用的混乱(即按 $4 进行预排序),或者使用 GNU 对输入进行分块split

预分选:

printf "%s\n" {0..499}\ 2\ 3\ {0..1}{0..9}{0..9}{0..9} | 
  sort -k 4 | time gawk -f a.awk

(您可能需要调整sort选项以同意awk的字段编号)

在大约 4.0 秒时,这甚至比第一种情况更快,因为文件处理被最小化。 (请注意,对大文件进行排序可能会使用$TMPDIR或中的磁盘临时文件/tmp。)

split

printf "%s\n" {0..499}\ 2\ 3\ {0..1}{0..9}{0..9}{0..9} | 
  time split -l 1000 --filter "gawk -f a.awk"

这大约需要 38 秒(因此您可以得出结论,即使启动 1000 个进程的开销也gawk小于低效的内部 FD 处理)。在这种情况下你必须使用>>而不是>在 awk 脚本中,否则每个新进程都会破坏之前的输出。 (如果您重新调整代码来调用,同样的警告也适用close()。)

您当然可以结合使用这两种方法:

printf "%s\n" {0..499}\ 2\ 3\ {0..1}{0..9}{0..9}{0..9} | 
  time split -l 50000 --filter "sort -k 4 | gawk -f a.awk"

对我来说,这大约需要 4 秒,调整分块 (50000) 可以让您在进程/文件处理开销与sort磁盘使用要求之间进行权衡。 YMMV。

如果您提前知道输出文件的数量(并且不是太大),您可以使用 root 来增加(例如ulimit -n 8192,然后su自己),或者您也可以一般调整限制,请参阅如何增加所有进程的打开文件限制?。该限制将由您的操作系统及其配置(如果不幸的话,可能还有 libc)决定。

相关内容