tee 不可预测行为的解释

Arc*_*ton 1 bash io-redirection tee

在测试对程序重复执行的输出求和的脚本时,我遇到了我不理解的行为。要重现它,请创建文本文件out,它代表我的程序的输出,以及sum保存先前执行返回的值的总和的文件,该文件作为 的副本开始out

cat > out << EOF
2 20
5 50
EOF
cp out sum
Run Code Online (Sandbox Code Playgroud)

奇怪的事情发生在跑步

paste out sum | awk '{$1 += $3; $2 += $4; NF = 2; print}' | tee sum
Run Code Online (Sandbox Code Playgroud)

几次(可能需要 15-20 次)。每次运行时,此命令应将sum相应值中的值相加out并将结果写回sum. 我得到的是它工作的次数不可预测,然后sum恢复到

2 20
5 50
Run Code Online (Sandbox Code Playgroud)

我后来了解到我无法将输出重定向或 tee 输出到我正在处理的同一个文件,并使用临时文件解决了这个问题,但这种行为让我感到困惑:

  • 为什么完全… | tee sum有效(即使只进行有限次数的迭代),而… > sum从不覆盖sum

  • 为什么它不能以可预测的次数工作?

ilk*_*chu 7

这个,

paste out sum | awk ... | tee sum
Run Code Online (Sandbox Code Playgroud)

有竞争条件。paste打开sum读取它,tee打开它进行写入,截断它。shell 几乎同时启动这两个文件,因此取决于哪个人先打开文件。

当然,在实践中,shell 必须一次启动一个实用程序,以某种特定的顺序。它可能从左到右这样做,所以paste可能有更好的机会先行,但这是一个实现细节,无论如何操作系统调度程序决定什么时间运行。

如果paste先走,它会打开文件,数据仍然完好无损,并且可能也有足够的时间来读取数据。如果teepaste读取文件之前打开文件,则会paste看到一个空文件。

这里,

paste out sum | awk ... > sum
Run Code Online (Sandbox Code Playgroud)

外壳打开sum以进行写入,将其截断。它可能与开始并行执行此操作paste,但由于截断sum不涉及启动另一个实用程序,因此它可能首先发生。(我不确定是否有关于处理重定向和在这样的管道中启动命令的顺序的规则,但我不会指望它。)

有一个工具sponge可以解决这个问题(以及关于它的十几个问题)。它收集它获得的输入,并且仅在输入关闭后才写入它。这应该已sum正确更新,始终:

paste out sum | awk ... | sponge sum
Run Code Online (Sandbox Code Playgroud)