我有這三個重定向 stdin/stdout 的範例,其中只有一個按預期方式工作。如果有人能向我解釋這一點,我會很高興。
目標是對 file1 中的內容進行排序並將變更儲存到同一文件中。
排序文件 1 | tee file1 > /dev/null --------> 它有效
排序文件 1 | tee file1 --------> file1 的內容將會被刪除
排序文件 1 | tee file1 > file2 --------> file1 的內容將會被刪除
附言。 tee 將標準輸入複製到每個文件,也複製到標準輸出。
第一個例子的工作原理是什麼?
答案1
根據我在 Debian Wheezy 上的測試,所有 3 個場景都可能導致兩種結果(file1 被排序並寫回自身,或者沒有任何內容被排序並且沒有任何內容寫入 file1。
我相信這是正常行為,來自 Linux 處理文件的方式。考慮一下該命令 - sort 命令開始讀取 file1 並立即將其輸出傳送至 tee。 Tee 讀取輸出,將其寫回 file1 並將其列印到 /dev/null。如果 sort 夠快來讀取整個檔案1,則 tee 會得到排序的輸出。但是,如果 tee 獲得了對文件的鎖定,它會刪除它(tee 總是刪除輸出文件,除非使用追加選項),這幾乎就是所有 3 個場景中發生的情況。
為了使其更短,可以說,有時排序速度不夠快,無法讀取 file1。在這種情況下,tee 在 sort 可以讀取檔案之前擦除該檔案。
我建議採用以下程序:
cat file1 | sort > /tmp/sorting.tmp; mv /tmp/sorting.tmp file1
如果您想在 stdout 上查看排序後的輸出,請按如下操作:
cat file1 | sort | tee /tmp/sorting.tmp; mv /tmp/sorting.tmp file1
在多處理器系統上讓 2 個不同的命令處理 1 個檔案並不是一個好主意 - 您永遠無法確定哪個命令首先執行。在單執行緒系統上,行為會有所不同 - 順序的。
答案2
我懷疑這種行為是可預測的(而且肯定不會依賴它)。該tee
命令可能會啟動一個新進程以將其輸入傳送到「其他」目的地。作業系統將「緩衝」輸出,直到它到達建立目標檔案並將其臨時緩衝區寫入該檔案的位置。這種情況發生(並覆蓋原始程式碼)的確切時刻可能取決於:
- 檔案的大小和緩衝區的可用內存
- 經過的時間
- 如果從管道輸入
tee
完成
這比bash
:這是程式啟動的方式bash
。 shell 只是解釋您輸入的命令,並啟動執行命令所需的程式。 shell 無法控制每個程式如何運作,更無法控制這些程式如何互動。要求一個程式(或一組程式)從輸入檔案中取得數據,並在同一個句子中將結果寫入到同一輸入檔案中,這是使用者的責任。
不要忘記bash只是用戶命令的解釋器:它只是shell
圍繞作業系統將用戶意圖轉換為系統呼叫。
這是記錄在案, 也!或者這封郵件,它解決了類似的問題。或這個StackOverflow線程。或者這個伺服器故障線程。
stdin
請注意,如果您從檔案: 中取得輸入命令,則重定向 : $ myprog < commandfile
也可能會發生這種情況。如果myprog
寫入命令文件,則不能保證所有commandfile
命令都會執行。
一個非常基本的類比是這樣的指令列表:
- Execute the instructions step by step
- Dip this instruction list in a bucket of black paint
- Type in the following commands:
find /etc -type f -exec cat '{}' \; | tr -c '.[:digit:]' '\n' \
| grep '^[^.][^.]*\.[^.][^.]*\.[^.][^.]*\.[^.][^.]*$'
我想你會先影印一份嗎? (命令取自進階 Bash 腳本指南)
答案3
那麼您希望保留文件的原始內容,同時附加更改後的文件嗎?
預設情況下,tee 會覆蓋寫入,請嘗試使用 -a 標誌將變更附加到檔案中。
答案4
sort file1 | tee file1 > tmp && mv file1 original && mv tmp file1
您可以將檔案寫入佔位符,將原始檔案重新命名為備份,然後將佔位符移至原始檔案。