重定向

时间:2016-12-28 22:16:54

标签: linux bash redirect io filesystems

如果目录中包含几百万个文件,我们想从这些文件中提取一些数据。

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 cmdxargs 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平行行为。

2 个答案:

答案 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"|"运行时,awkparallel代码周围的引号将被取消引用。

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

文件以运行进程的日期和时间命名。