在测试对程序重复执行的输出求和的脚本时,我遇到了一种我不理解的行为。要重现它,请创建文本文件out
,它代表我的程序的输出,并且sum
该文件保存了先前执行返回的值的总和,并且开始时是 的副本out
,
cat > out << EOF
2 20
5 50
EOF
cp out sum
跑步时发生奇怪的事情
paste out sum | awk '{$1 += $3; $2 += $4; NF = 2; print}' | tee sum
几次(可能需要 15-20 次)。每次运行时,此命令都应将sum
相应值中的值添加到 中out
,并将结果写回sum
. 我得到的是它工作了不可预测的次数,然后又sum
恢复到
2 20
5 50
后来我了解到我无法将输出重定向或发送到我正在处理的同一个文件,并使用临时文件解决了这个问题,但这种行为仍然让我感到困惑:
为什么根本不
… | tee sum
工作(即使只是有限次数的迭代),而… > sum
从不覆盖sum
?为什么它不能按可预测的次数工作?
这个,
有竞争条件。
paste
打开sum
阅读它,tee
打开它进行写作,截断它。shell 几乎同时启动,因此由哪一个先打开文件取决于机会。当然在实践中,shell 必须以某种特定的顺序一次启动一个实用程序。它可能从左到右执行此操作,因此
paste
可能有更好的机会先行,但这是一个实现细节,无论如何操作系统调度程序决定什么时候运行。如果
paste
先走,它会打开数据仍然完好无损的文件,并且可能也有足够的时间来读取数据。如果在读取tee
文件之前打开文件paste
,则会paste
看到一个空文件。这里,
外壳打开
sum
用于写入,截断它。它可能与启动并行执行此操作paste
,但由于截断sum
不涉及启动另一个实用程序,因此它可能首先发生。(我不确定是否有关于处理重定向和在这样的管道中启动命令的顺序的规则,但我不会指望它。)有一个工具
sponge
可以解决这个问题(以及关于它的十几个问题)。它收集它获得的输入,并且仅在输入关闭后才写入。这应该sum
始终正确更新: