【问题标题】:xargs: losing output when redirecting stdout to a file in parallel modexargs:以并行模式将标准输出重定向到文件时丢失输出
【发布时间】:2015-12-03 16:58:22
【问题描述】:

我在并行模式下使用 GNU xargs(版本 4.2.2),并且在重定向到文件时似乎可靠地丢失了输出。重定向到管道时,它似乎可以正常工作。

以下 shell 命令演示了该问题的 minimum, complete, and verifiable example。我使用xargs 生成 2550 个数字,将其拆分为 100 个参数的行,每行总共 26 行,其中第 26 行仅包含 50 个参数。

# generate numbers 1 to 2550 where each number is on its own line
$ seq 1 2550 > /tmp/nums
$ wc -l /tmp/nums
2550 /tmp/nums

# piping to wc is accurate: 26 lines, 2550 args
$ xargs -P20 -n 100 </tmp/nums | wc
     26    2550   11643

# redirecting to a file is clearly inaccurate: 22 lines, 2150 args
$ xargs -P20 -n 100 </tmp/nums >/tmp/out; wc /tmp/out
     22  2150 10043 /tmp/out

我认为问题与底层 shell 无关,因为 shell 将在命令执行之前执行重定向并等待 xargs 完成。在这种情况下,我假设 xargs 在刷新缓冲区之前完成。但是,如果我的假设是正确的,我不知道为什么在写入管道时这个问题不会出现。

编辑:

在shell中使用&gt;&gt;(创建/附加到文件)时出现,问题似乎没有表现出来:

# appending to file
$ >/tmp/out
$ xargs -P20 -n 100 </tmp/nums >>/tmp/out; wc /tmp/out
     26    2550   11643

# creating and appending to file
$ rm /tmp/out
$ xargs -P20 -n 100 </tmp/nums >>/tmp/out; wc /tmp/out
     26    2550   11643

【问题讨论】:

  • 我在这两种情况下都得到了准确的输出。 Shell&gt; wc -l /tmp/nums 2550 /tmp/nums Shell&gt; xargs -P20 -n 100 &lt;/tmp/nums | wc 26 2550 11643 Shell&gt; xargs -P20 -n 100 &lt;/tmp/nums &gt;/tmp/out; wc /tmp/out 26 2550 11643 /tmp/out Shell&gt;
  • 如果你清空输出文件然后使用&gt;&gt;而不是&gt;重定向,你能可靠地得到正确的结果吗?如果是这样,那就有某种解释。
  • @JonathanLeffler:看起来你是对的。使用&gt;&gt;,问题不会出现。我尝试提前创建文件并使用“>”进行重定向(截断现有文件),但问题似乎再次出现。
  • 当您使用&gt; 重定向时,/tmp/out 的开头会出现哪些数字?它们是像 1、2、3 这样的数字,还是像 2001、2002、2003 这样的数字?我在想出一个合理的机制来解决这个问题时遇到了一些问题。管道和附加行为很容易解释。但是&gt; 的行为应该基本相同,我想知道事情是如何被破坏的。你有trussstrace 可用吗?如果是这样,看看xargs 进程做了什么可能会很有启发意义(但不是——至少首先——它的子进程做了什么)。 [...继续...]
  • […continuation…] 运行strace -o xargs.log xargs -P 20 -n 100 &lt;/tmp/nums &gt; /tmp/out 后,xargs.log 中是否有任何有用的信息?我正在考虑文件描述符 1 上的 lseek() 之类的东西,但我不确定这是否合理。一个问题可能是实际上是孩子造成了恶作剧。在这种情况下,您需要使用“关注孩子”选项 (-f) 来查看导致问题的原因。但输出量会更大。我在 Mac OS X 10.10.5 和 Ubuntu 14.04 LTS(在 Mac OS X 下的 VM 中运行)都得到了“正确”的输出。

标签: bash shell xargs


【解决方案1】:

您的问题是由于不同进程的输出混合造成的。它显示在这里:

parallel perl -e '\$a=\"1{}\"x10000000\;print\ \$a,\"\\n\"' '>' {} ::: a b c d e f
ls -l a b c d e f
parallel -kP4 -n1 grep 1 > out.par ::: a b c d e f
echo a b c d e f | xargs -P4 -n1 grep 1 > out.xargs-unbuf
echo a b c d e f | xargs -P4 -n1 grep --line-buffered 1 > out.xargs-linebuf
echo a b c d e f | xargs -n1 grep 1 > out.xargs-serial
ls -l out*
md5sum out*

解决方案是缓冲每个作业的输出 - 在内存或 tmpfiles 中(就像 GNU Parallel 所做的那样)。

【讨论】:

  • 我同意绝对无法控制 stdout 上的输出混合(除非子进程的 write()s 在大小、原子和应用程序允许输出混合方面有限制),但是这并不能解释在我的示例和您的示例中都发生的 losing 输入。由于输出分组,我实际上切换到并行。
  • 那是由于同一个文件打开了多个文件描述符:如果他们一个接一个地写,没有问题。如果它们同时写入,它们将写入文件中的相同位置。这也解释了为什么如果您重定向到管道而不是文件时看不到问题:管道中没有文件位置。这也解释了为什么 >> 不会导致该行为。
【解决方案2】:

我知道这个问题是关于 xargs 的,但如果您一直遇到问题,那么 GNU Parallel 可能会有所帮助。您的 xargs 调用将转换为:

$ < /tmp/nums parallel -j20 -N100 echo > /tmp/out; wc /tmp/out
26  2550 11643 /tmp/out

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-08-31
    • 2016-06-26
    • 2013-10-09
    • 2014-11-22
    • 2015-05-23
    • 1970-01-01
    相关资源
    最近更新 更多