给定一个包含几百万个文件的目录,我们希望从这些文件中提取一些数据。
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在terminal中作为STDOUT打开的六个subprocess的输出时,数据看起来很好。 数据redirect到文件系统的时刻是出现损坏的时刻。
我试图追加下面的命令。
> myfile.txt
>> myfile.txt
| mawk '{print $0}' > myfile.txt
还有其他一些redirect的概念,或者在将xargs的输出“写入”磁盘之前将其汇集到每个版本中数据被破坏的磁盘上。
我是积极的原始文件不格式化。 我认为,当在terminal作为标准输出查看命令与xargs产生有效的输出,长达10分钟的盯着它吐文本…
本地磁盘是SSD …我正在读取和写入相同的文件系统。
为什么redirectfind /dir/ -type f -print0 | xargs -0 -n1 -P6 awk -F"|" '$2 ~ /string/{ print $3"|"$7 }'
find /dir/ -type f -print0 | xargs -0 -n1 -P6 awk -F"|" '$2 ~ /string/{ print $3"|"$7 }'
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
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
。
man xargs
提到这个问题:“请注意,这是由被调用的进程来正确管理对共享资源的并行访问,例如,如果他们中有不止一个人试图打印到stdout,则会以不确定的顺序(很可能混在一起)“
幸运的是,有一种方法可以使这个操作快一个数量级,同时也解决了这个问题:
find /dir/ -type f -print0 | xargs -0 awk -F"|" '$2 ~ /string/{ print $3"|"$7 }'
为什么?
-P6
正在洗牌您的输出,所以不要使用它。 xargs -n1
为每个文件启动一个awk
进程,而没有n1
, xargs
启动很少的awk
进程,如下所示:
files | xargs -n1 awk => awk file1 awk file2 ... awk fileN vs files | xargs awk => awk file1 file2 ... fileN # or broken into a few awk commands if many files
我运行你的代码约20K的文本文件,每个-n1 -P6
大小有和没有-n1 -P6
:
with -n1 -P6 23.138s without 3.356s
如果你想要没有xargs
的标准输出的parallel
,可以使用gnu parallel
(也可以用Gordon Davisson的建议),例如:
find /dir/ -type f -print0 | parallel --xargs -0 -q awk -F"|" '$2 ~ /string/{ print $3"|"$7 }'
注意: -q
是引用命令字符串所必需的,否则引号在-F"|"
而awk
代码在parallel
运行时不会被引用。
parallel
节省了一点时间,但不像开沟一样 – -n1
:
parallel 1.704s
ps:介绍一只cat
(Matt在他的回答中)比xargs awk
快得多:
xargs awk 3.356s xargs cat | awk 3.036s
我只会做以下几点:
cat /${dir}/* | awk '$2 ~ /string*/{ print $3 "|" $7 }' >> `date`.txt
文件以运行进程的日期和时间命名。