題 你為什麼要`cat / dev / null> / var / log / messages`?


就這個 bash腳本示例頁面 作者提出了這個腳本:

# Cleanup
# Run as root, of course.

cd /var/log
cat /dev/null > messages
cat /dev/null > wtmp
echo "Log files cleaned up."

你為什麼要這樣做 cat /dev/null 什麼?我無法理解這裡的意圖(就像使用它一樣) while TRUE; sleep 1; elihw 對於 {some busy program}?)。然而,作者稱之為“沒什麼不尋常的”。


75
2017-12-06 19:40


起源




答案:


你通常 cat /dev/null > [something] 當您想要擦除文件內容的同時確保實際文件狀態中斷的風險絕對沒有。該文件的內容將被清除 cat /dev/null 然而,文件本身 - 因為它存在並且它所駐留的文件系統已知 - 將仍然具有相同的inode編號,所有權和權限。

對於日誌文件,可能是日誌文件本身被另一個進程標記為“正在使用”。所以做 - 例如 - 一個 rm /var/log/messages && touch /var/log/messages 會破壞其他進程,並可能導致運行進程窒息。意味著某個進程以某種方式鎖定到連接到該文件的特定inode號 /var/log/messages 可能突然驚慌失措地說:“嘿!發生了什麼事 /var/log/messages!“即使文件仍在那裡。更不用說錯誤地重新創建所有權和權限的潛在問題。

由於這種使用/文件狀態的不確定性使用 cat /dev/null > [something] 想要清除日誌但不想要的系統管理員首選 可能 干擾現有流程的運作。

也, 在您鏈接到的頁面的上下文中 作者陳述如下:

這裡沒有什麼不尋常的,只有一組命令可以   很容易從命令行上一個一個地調用   控制台或終端窗口。放置的優點   腳本中的命令遠遠超出了不必重新鍵入時間和   再次。

因此,作者提到的“沒有什麼不尋常的”是關於特定bash腳本的整體概念:它只是一組簡單的命令,可以很容易地從命令行運行但放在文本文件中避免不得不一遍又一遍地重新輸入。


39
2017-12-06 19:50



@jlliagre唯一“保持活力”的是問題的背景,其重點是原作者為什麼這樣做。如果你對消除“神話”感到慷慨激動,請發表一個答案,為原作者的編碼方法提供背景,以及為什麼你認為它可以被其他方法取代的觀點。 - JakeGould
@jlliagre Cyrus的答案沒有解決原作者為什麼會使用該方法的核心問題,也沒有解決其背後的原因。提到的教程很老,而且被合理地接受了。它不是不正確的,也不會延續所謂的“城市傳說”。相反,它是一個人編碼的方式與另一個人編碼的方式。就如此容易。這是一種風格問題,對可靠性或性能沒有負面影響。 - JakeGould
truncate -s 0 會做同樣的事情,而不是慣用。但是,shell程序員是一個保守的群體,人們可能會遇到足夠老或古怪的系統缺乏該命令。 - Schwern
@skift cat /dev/null > /foo/bar 截斷文件; echo "" > /foo/bar 截斷它然後寫一個換行符。 - David
此方法相對於rm&touch的另一個優點是保留了所有權和權限。 - jjmontes


你為什麼要cat / dev / null到任何東西?

您將這樣做以截斷文件內容,同時保持inode完整。所有打開用於讀取或寫入的文件的程序都不會受到影響,因為文件大小將重置為零。

經常發現的偽造替代方法是刪除文件,然後再次創建它:

rm file
touch file

或類似的:

mv file file.old
gzip file.old
touch file

問題是這些方法不會阻止舊文件被刪除時打開已刪除文件的任何進程保留。原因是在Unix文件系統下,當您刪除文件時,您只將其名稱(路徑)與其內容(inode)取消鏈接。只要存在打開讀取或寫入的進程,inode就會保持活動狀態。

這會導致一些負面影響:文件刪除後寫入的日誌會丟失,因為沒有直接/可移植的方式來打開已刪除的文件。只要進程正在寫入已刪除的文件,其內容仍在文件系統上使用空間。這意味著如果您刪除/創建文件,因為它正在填滿您的磁盤,磁盤將保持填充狀態。解決後一問題的一種方法是重新啟動記錄器進程,但您可能不希望對關鍵服務執行此操作,並且中間日誌將最終丟失。由於您創建的文件可能與原始文件不具有相同的權限,所有者和組,因此還存在副作用。例如,這可能會阻止日誌分析器讀取新創建的文件,或者更糟糕的是,阻止日誌記錄過程編寫自己的日誌。

第一種方法, cat /dev/null > file 正確實現目標 然而,儘管有一個頑強的城市傳說,它的 cat /dev/null 部分絕對沒有用。 它會打開一個偽文件,它在設計上是空的,它無法從中讀取任何內容,最後只是退出。使用此命令會浪費擊鍵,字節,系統調用和CPU週期,並且可以通過毫無疑問更快的no-op命令進行替換而無需任何功能更改 : 甚至,對於大多數砲彈,根本沒有命令。

讓我試著用一個比喻來解釋一下如何無用 cat /dev/null 是。假設你的目標是清空杯子。

  • 你先從中取出任何液體。這就足夠了(正是如此)> file)確實給出了重定向始終首先處理的事實。

  • 然後,你挑一個空瓶子(/dev/null)並將其倒入空杯中(cat)。這是毫無意義的一步......

如果你讀 你的鏈接文件 最後,您可能會注意到該行的增強版本中的註釋:

    cat / dev / null> wtmp# ':> wtmp'和'> wtmp'具有相同的效果。

他們確實;太糟糕了 cat /dev/null 保存在代碼中。

這意味著以下代碼將適用於所有常見的shell(兩者都有 csh 和 sh 家庭):

cd /var/log
: > messages
: > wtmp
echo "Log files cleaned up."

這將適用於使用Bourne語法的所有shell,例如 ashbashkshzsh 和喜歡:

cd /var/log
> messages
> wtmp
echo "Log files cleaned up."

但請注意,對於古老的POSIX前Bourne shell,這些命令中的任何一個,包括 cat /dev/null 如果文件後來被附加到其上的仍在運行的shell腳本寫入,則不會截斷該文件。而不是零字節文件,這將是一個大小不變的稀疏文件。如果文件是由進程在寫入之前尋找其認為是當前位置的進程寫入的,則會發生同樣的情況。

還要注意通常建議截斷文件的一些替代解決方案確實存在缺陷。

  • 以下兩個都不能完成這項工作。生成的文件不為空,但包含空行。這會破壞日誌文件 wtmp 存儲固定寬度記錄。

    echo > file
    echo "" > file
    
  • 下一個基於BSD sh 選項不可移植,POSIX沒有為echo指定任何允許的選項,因此您最終可能會得到一個包含“-n“:

    echo -n > file
    
  • 那個不能通過使用System V移植 sh 逃脫序列。有些shell會創建一個包含“\c“:

    echo "\c" > file
    
  • 那個使用專門用來完成工作的命令。問題是使用 truncate 不可移植,因為Unix / Linux系統中可能缺少此命令(POSIX未指定)。

    truncate -s 0
    

最後,這裡有幾個可移植的替代方案,可以正常完成工作:

  • 將空字符串顯式打印到文件中:

    printf "" > file
    
  • 使用 true 命令嚴格等同於無操作命令 : 雖然更具可讀性:

    true > file
    

121
2017-12-06 21:24



@JonathanLeffler我相信這是目前的一部分 csh 實現雖然沒有必要記錄。來自Solaris csh  手冊頁 : Null command. This command is interpreted, but performs no action.。我發現兩者都沒有提到相同的 tcsh 手冊頁或原始BSD csh 但這種語法可能總是有效。 - jlliagre
寫作的一個原因 cat /dev/null 是讓你的意圖明確。 - Davidmh
@Davidmh那是明智的,但你的陳述並不能抵制事實核查。我已經觀察了這個殼成語可能已經有幾十年了。當我有機會向作者詢問其背後的推理時,我 總是 關於使用的理論不完整 /dev/null 注入零字節是一種有效的方法,無論如何比使用更可靠 : 或無。在這個頁面中,Cyrus的答案雖然簡潔但直截了當,但JakeGould是一個挑戰事實的人。 cat /dev/null 是一個無操作(見我們的評論)已經至少有四票。 - jlliagre
@jlliagre我對shell的了解非常基礎,所以我可能不是最好的人口目標;但是裸露的 > 要么 : 不清楚。我同意,由於它的使用,神話已被傳播是一種恥辱。 - Davidmh
@Davidmh如果您在重定向之前確實需要明確的東西,我會建議 printf "" > file 這是便攜式(POSIX)和輕量級的,通常作為shell內置實現。 - jlliagre


將文件置於零大小是一種麻煩的方法。

cd /var/log
> messages
> wtmp
echo "Log files cleaned up."

6
2017-12-06 19:44



此語法不適用於每個shell。 - reinierpost
正確。這個問題只標有“bash”。 - Cyrus
@reinierpost它將適用於所有Bourne風格的砲彈,不是嗎?我們不用擔心 csh。 - Barmar
: > messages也有效。 : 要么 true 對於沒有打印並且返回true的命令,更明顯的選擇。 - Peter Cordes


截斷打開的文件。這是等同的,更容易理解:

echo -n > /var/log/messages

(添加-n以避免換行)


-3
2017-12-07 18:29



這確實更容易理解,但不幸的是不等同。它甚至會破壞像中的功能 wtmp 案件。看到我更新的答案。 - jlliagre
echo -n避免換行。 - bbaassssiiee
如果你肯定會使用它確實會 bash, -n 否則不能保證在所有Bourne shell中都能正常工作。 - jlliagre