给定一个包含几百万个文件的目录,我们想从这些文件中提取一些数据。
查找/dir/-type f | awk -F"|"'$2 ~/string/{ print $3"|"$7 }' > the_good_stuff.txt
这永远不会扩展,所以我们引入了 xargs。
查找/dir/-type f -print0 | xargs -0 -n1 -P6 awk -F"|"'$2 ~/string/{ 打印 $3"|"$7 }'
无论我们运行多长时间,它都会产生有效的输出。太棒了,让我们通过在该命令上附加 > the_good_stuff_from_xargs.txt
将其写入文件。除了现在文件包含损坏的行。
令我印象深刻的是,在我的终端中查看 xargs 作为标准输出打开的六个子进程的输出时,数据看起来很好。数据被重定向到文件系统的那一刻就是出现损坏的时候。
我尝试在命令中附加以下内容。
> 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
,这是所有答案中效果最好的。
我运行的最终命令看起来像。
时间查找 -L/dir/-type f -mtime -30 -print0 |并行 -0 -X awk -f manual.awk > the_good_stuff.txt
Awk 很难,所以我将 -F"|"
移到了命令本身。默认情况下,并行会在盒子上的每个核心启动一个作业,如果需要,您可以使用 -j
将作业数设置得更低。
用真正的科学术语来说,这是一个巨大的速度提升。 6 分钟后 6 分钟完成了 10%,因此可能会在一小时内完成。
一个问题是,您必须确保以 parallel
运行的命令不会尝试写入文件...这会有效地绕过并行对其运行的作业执行的输出处理!
最后,在没有 -X
的情况下,并行行为类似于 xargs -n1
。
最佳答案
man xargs
提到了这个问题:“请注意,正确管理对共享资源的并行访问取决于被调用进程。例如,如果其中多个进程试图打印到标准输出,输出将以不确定的顺序产生(并且很可能混淆)”
幸运的是,有一种方法可以使这个操作快一个数量级,同时解决 mangling 问题:
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,有和没有 -n1 -P6
:
with -n1 -P6 23.138s
without 3.356s
如果你想在没有 xargs
的标准输出改组的情况下进行并行处理,请使用 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
关于linux - 命令输出在重定向时被破坏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41370466/