Bash

三通不可預知行為的解釋

  • August 26, 2018

在測試對重複執行程序的輸出求和的腳本時,我遇到了我不理解的行為。要重現它,請創建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 輸出到我正在處理的同一個文件並使用臨時文件解決了這個問題,但這種行為讓我感到困惑:

  • 為什麼完全… | tee sum有效(即使僅用於有限數量的迭代),而… > sum從不覆蓋sum
  • 為什麼它不能按可預測的次數工作?

這,

paste out sum | awk ... | tee sum

有競爭條件。paste打開sum讀取它,tee打開它寫入,截斷它。shell 大約在同一時間啟動兩者,因此取決於哪個先打開文件的機會。

當然,在實踐中,shell 必須以某種特定的順序一次啟動一個實用程序。它可能從左到右執行此操作,因此paste可能有更好的先行機會,但這是一個實現細節,無論如何,作業系統調度程序決定什麼時候執行。

如果paste先走,它會打開數據仍然完好的文件,並且可能還有足夠的時間來讀取數據。如果在讀取tee文件之前打開文件paste,則會paste看到一個空文件。

這裡,

paste out sum | awk ... > sum

外殼打開sum以進行寫入,將其截斷。它可能與啟動並行執行此操作paste,但由於截斷sum不涉及啟動另一個實用程序,因此它可能首先發生。(我不確定是否有關於在這樣的管道中處理重定向和啟動命令的順序的規則,但我不會指望它。)

有一個工具sponge可以解決這個問題(以及關於它的十幾個問題)。它收集它獲得的輸入,並且僅在輸入關閉後才寫入。這應該sum始終正確更新:

paste out sum | awk ... | sponge sum

引用自:https://unix.stackexchange.com/questions/464943