【发布时间】:2017-05-13 05:36:24
【问题描述】:
给定一个包含几百万个文件的目录,我们想从这些文件中提取一些数据。
find /dir/ -type f | awk -F"|" '$2 ~ /string/{ print $3"|"$7 }' > the_good_stuff.txt
这将永远无法扩展,因此我们引入了 xargs。
find /dir/ -type f -print0 | xargs -0 -n1 -P6 awk -F"|" '$2 ~ /string/{ print $3"|"$7 }'
无论我们运行多长时间,这都会产生有效的输出。 Sweet 所以让我们通过在该命令上附加> the_good_stuff_from_xargs.txt 将其写入文件。除了现在文件包含错位的行。
令我印象深刻的是,在我的终端中查看 xargs 作为 STDOUT 打开的六个子进程的输出时,数据看起来很好。数据被重定向到文件系统的那一刻就是出现损坏的时候。
我尝试在命令中附加以下内容。
> myfile.txt
>> myfile.txt
| mawk '{print $0}' > myfile.txt
以及在将 xargs 的输出写入磁盘之前重定向或以其他方式“汇集”输出的各种其他概念,每个版本中的数据都已损坏。
我确信原始文件没有格式错误。我很肯定,当在终端中将其视为标准输出时,带有 xargs 的命令会产生有效输出长达 10 分钟的盯着它吐出的文本...
本地磁盘是 SSD...我正在从同一个文件系统读取和写入。
为什么重定向find /dir/ -type f -print0 | xargs -0 -n1 -P6 awk -F"|" '$2 ~ /string/{ print $3"|"$7 }'的输出会导致数据格式错误?
编辑
我目前无法安装 unbuffer,但 stdbuf -oL -eL 将命令输出修改为行缓冲,因此理论上应该做同样的事情。
stdbuf xargs cmd 和xargs stdbuf cmd 我都试过了,都导致断线非常严重。
-P6 是必需的,以便此命令在任何合理的时间内完成。
编辑 2
澄清一下...xargs 和 -P6 标志是解决问题的必要条件,因为我们正在处理的目录有数百万个必须扫描的文件。
显然,我们可以删除 -P6 或以其他方式停止同时运行多个作业,但这并不能真正回答 为什么 输出会被破坏的问题,也不是一种现实的方法 如何在大规模完成任务的同时将输出恢复到“正确”状态。
解决方案
使用parallel 提到的已接受答案是所有答案中效果最好的。
我运行的最后一个命令看起来像。
time find -L /dir/ -type f -mtime -30 -print0 | parallel -0 -X awk -f manual.awk > the_good_stuff.txt
awk 很困难,所以我将-F"|" 移到了命令本身。默认情况下,并行将在机器上为每个内核启动一个作业,如果需要,您可以使用-j 将作业数设置为更低。
用真正的科学术语来说,这是一个巨大的速度提升。花费了无法衡量的小时数(可能超过 6 小时)的工作在 6 六分钟后完成了 10%,因此可能会在一个小时内完成。
一个问题是,您必须确保在 parallel 中运行的命令没有尝试写入文件...这实际上绕过了并行对其运行的作业执行的输出处理!
最后没有-X 的并行行为类似于xargs -n1。
【问题讨论】:
-
标准输出在写入终端时是行缓冲的,但在写入管道或文件时是完全缓冲的。
-
删除
-P6;这会导致 6 个异步进程随机写入您的输出,并在缓冲区填满时写入部分行,并且不同的进程在不同的点写入不同的部分行,等等。如果您必须使用-P6,您需要拥有6 个进程写入不同的文件,这样它们就不会践踏彼此的输出。这反过来可能意味着运行一个 shell 脚本,该脚本运行awk并将 I/O 重定向到一个单独的文件(也许使用mktemp,其名称基于脚本的 PID)。 -
听起来您应该使用
parallel而不是xargs,因为它管理命令的输出以避免此类麻烦。见this previous question。 -
当然你应该使用 GNU
parallel -
parallel -q引用命令字符串,以便您可以使用原始 awk-F"|"而不是单独的.awk文件。
标签: linux bash redirect io filesystems