linux - 命令输出在重定向时被破坏

标签 linux bash redirect io filesystems

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

查找/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 cmdxargs 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/

相关文章:

python - Django注册打开其他模板

c++ - 使用 OpenGL 的 GL_POINT_SIZE_RANGE 解决方法?

linux - 如何从命令行从 Linux 服务器下载文件?

linux - 设置对远程服务器执行客户端证书身份验证的代理

linux - 编写脚本,为每个可写的普通类型文件制作编号列表

apache - 如何显示单个页面的 503 错误

c - C中如何检查服务器端是否建立连接

bash - 如何编写带有可选输入参数的 bash 脚本?

bash - BASH 中的 `trap cleanup INT EXIT` 问题

php - 使用 Alamofire 请求 Web API 时出现重定向错误 301 和 302