如果目录中包含几百万个文件,我们想从这些文件中提取一些数据。
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 }'
无论我们运行多长时间,都会生成有效输出。很好,我们可以通过在该命令上附加> the_good_stuff_from_xargs.txt
将其写入文件。除了现在文件包含损坏的行。
令我印象深刻的是,在查看xargs在我的终端中作为STDOUT打开的六个子进程的输出时,数据看起来很好。数据重定向到文件系统的那一刻就是腐败出现的时候。
我尝试使用以下内容附加命令。
> myfile.txt
>> myfile.txt
| mawk '{print $0}' > myfile.txt
以及其他各种重定向或以其他方式“汇集”的概念。 xargs的输出在写入磁盘之前,每个版本中的数据都被破坏了。
我肯定原始文件没有格式错误。我很肯定当在终端中查看为stdout时,带有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"|"
移到了命令本身。默认情况下,parallel会在框中为每个核心启动一个作业,如果需要,可以使用-j
设置较低的作业数。
从科学的角度来说,这是一个巨大的速度提升。花费了不可测量的小时数(可能是6小时)是在6分钟后完成10%,所以可能会在一小时内完成。
一个问题是你必须确保在parallel
中运行的命令没有尝试写入文件...这有效地绕过并行对其运行的作业执行的输出处理! / p>
最后没有与-X
类似的xargs -n1
平行行为。
答案 0 :(得分:2)
man xargs
提到了这个问题:“请注意,由被调用的进程来正确管理对共享资源的并行访问。例如,如果有多个人尝试打印到stdout,那么ouptut将会以不确定的顺序生产(很可能是混淆的)“
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文本文件上运行你的代码,每个文本文件大小为20k,有{J}和不-n1 -P6
:
with -n1 -P6 23.138s
without 3.356s
如果你想要没有xargs
stdout shuffling的并行性,可以使用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
答案 1 :(得分:0)
我会做以下事情:
cat /${dir}/* | awk '$2 ~ /string*/{ print $3 "|" $7 }' >> `date`.txt
文件以运行进程的日期和时间命名。