題 當目標文件不存在時,重定向是否與`>>`等效於`>`?


考慮像Bash或sh這樣的shell。兩者之間的基本區別 > 和 >> 在目標文件存在的情況下表現出來:

  • > 將文件截斷為零大小,然後寫入;
  • >> 不截斷,它寫入(追加)到文件的末尾。

如果文件不存在,則創建零大小;然後寫到。這對兩個運營商都是如此。當目標文件尚不存在時,運算符似乎是等價的。

他們真的嗎?


79
2017-07-23 08:42


起源




答案:


TL;博士

沒有。 >> 實質上是“總是尋求文件結束”而 > 維護指向最後寫入位置的指針。


完整答案

(注意:我在Debian GNU / Linux 9上完成的所有測試)。

另一個區別

不,他們不等同。有 另一個 區別。無論目標文件是否存在,它都可能表現出來。

要觀察它,請運行一個生成數據並重定向到文件的進程 > 要么 >> (例如。 pv -L 10k /dev/urandom > blob)。讓它運行並更改文件的大小(例如,使用 truncate)。你會看到的 > 保持其(增長)偏移 >> 總是貼在最後。

  • 如果將文件截斷為較小的大小(可以是零大小)
    • > 如果沒有發生任何事情,它會寫下它想要的偏差;在截斷偏移超出文件末尾之後,這將導致文件重新恢復其舊大小並進一步增長,缺少的數據將填充零(如果可能,以稀疏方式);
    • >> 將附加到新的末尾,文件將從其截斷的大小增長。
  • 如果放大文件
    • > 如果沒有發生任何事情,它會寫下它想要的偏差;在更改大小後,偏移量在文件內部的某處,這將導致文件停止增長一段時間,直到偏移量到達新的結束,然後文件將正常增長;
    • >> 將附加到新的結尾,文件將從其放大的大小增長。

另一個例子是追加(有一個單獨的 >>)當數據生成過程正在運行並寫入文件時,額外的東西。這類似於放大文件。

  • 生成過程 > 將寫入其所需的偏移量並最終覆蓋額外的數據。
  • 生成過程 >> 將跳過新數據並追加它(可能會出現競爭條件,兩個流可能會交錯,仍然不會覆蓋任何數據)。

在實踐中這有關係嗎?有 這個問題

我正在運行一個在stdout上產生大量輸出的進程。將其全部發送到文件[...]我可以使用某種日誌輪換程序嗎?

這個答案 說解決方案是 logrotate 同 copytruncate 這樣做的選項:

創建副本後,將原始日誌文件截斷,而不是移動舊日誌文件,也可以選擇創建新日誌文件。

根據我上面寫的,重定向 > 將立即使截斷的日誌變大。稀疏性將節省一天,不應浪費大量磁盤空間。然而,每個連續的日誌中都會有越來越多的前導零,這是完全沒必要的。

但如果 logrotate 在不保留稀疏性的情況下創建副本,這些前導零在每次復制時都需要越來越多的磁盤空間。我還沒有研究過工具的行為,它可能在運行時具有稀疏性或壓縮性(如果啟用了壓縮)。零仍然只能造成麻煩或充其量中立;他們沒什麼好處的。

在這種情況下使用 >> 代替 > 即使目標文件即將創建,也要好得多。


性能

正如我們所看到的,這兩個操作員的行為不僅在他們開始時而且在以後也有所不同。這可能會導致一些(微妙的)性能差異。目前我沒有任何有意義的測試結果來支持或反駁它,但我認為你不應該自動認為他們的表現一般都是一樣的。


107
2017-07-23 08:42



所以 >> 實質上是“總是尋求文件結束”而 > 維護指向最後寫入位置的指針。似乎它們的工作方式可能會有一些微妙的性能差異...... - Mokubai♦
在系統調用級別上, >> 使用 O_APPEND 國旗 open()。實際上, > 使用 O_TRUNC,而 >> 沒有。的組合 O_TRUNC | O_APPEND 也可能,shell語言不提供該功能。 - ilkkachu
@jjmontes,標準來源是POSIX: pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/... 但當然Bash的手冊也有重定向操作符的描述,包括它支持的非標準操作符: gnu.org/software/bash/manual/html_node/Redirections.html - ilkkachu
@ilkkachu我發現這很有意思,因為它解釋了有關O_APPEND的詳細信息,我在評論後想知道:): stackoverflow.com/questions/1154446/... - jjmontes
@Mokubai,任何理智的操作系統在打開時都會有文件長度,檢查標誌並將偏移移動到最後應該在所有其他簿記中消失。試圖模仿 O_APPEND 與 lseek() 在每個之前 write() 雖然會有所不同,但會有額外的系統調用開銷。 (當然它不會起作用,因為另一個過程可以 write() 介於兩者之間。) - ilkkachu