如何刪除給定目錄中最舊的目錄?
可能重複:
我有一個備份目錄,其中儲存
x
需要備份的其他目錄。我需要在另一個目錄移動到備份之前執行的東西,它將檢查目錄數量是否達到x
,如果達到,它將刪除最舊的目錄。它應該在
bash
腳本中完成。
解析的輸出
ls
是不可靠的。相反,用於
find
定位目錄並按sort
時間戳對它們進行排序。例如:IFS= read -r -d $'\0' line < <(find . -maxdepth 1 -type d -printf '%T@ %p\0' \ 2>/dev/null | sort -z -n) file="${line#* }" # do something with $file here
這一切在做什麼?
首先,這些
find
命令定位目前目錄(.
)中的所有目錄,而不是目前目錄的子目錄(-maxdepth 1
),然後列印輸出:
- 時間戳
- 空間
- 文件的相對路徑
- 一個 NULL 字元
時間戳很重要。
%T@
格式說明-printf
符分為T
,表示文件的“上次修改時間”(mtime)和,@
表示“自 1970 年以來的秒數”,包括小數秒。空格只是一個任意的分隔符。文件的完整路徑是為了我們以後可以參考它,NULL 字元是一個終止符,因為它是文件名中的非法字元,因此可以讓我們確定我們到達了路徑的末尾文件。
我已經包括在內
2>/dev/null
,因此使用者無權訪問的文件被排除在外,但有關它們被排除的錯誤消息被抑制。該
find
命令的結果是目前目錄中所有目錄的列表。該列表通過管道傳輸到該列表sort
,指示其執行以下操作:
-z
將 NULL 視為行終止符而不是換行符。-n
按數字排序由於 seconds-since-1970 總是上升,我們想要時間戳是最小數字的文件。第一個結果
sort
將是包含最小編號時間戳的行。剩下的就是提取文件名。
find
,管道的結果sort
通過程序替換傳遞給read
, 在那裡讀取它就像它是標準輸入上的文件一樣。在
read
我們將IFS
變數設置為空的上下文中,這意味著空格不會被不恰當地解釋為分隔符。read
被告知-r
,它禁用轉義擴展,並且-d $'\0'
,它使行尾分隔符為 NULL,匹配來自我們的find
,sort
管道的輸出。第一個數據塊,表示最舊的目錄路徑,前面是時間戳和空格,被讀入變數
line
。接下來,參數替換與表達式一起使用#*
,它簡單地將字元串開頭到第一個空格(包括空格)的所有字元替換為空。這會去除修改時間戳,只留下文件的完整路徑。此時文件名儲存在
$file
其中,您可以使用它做任何您喜歡的事情,包括rm -rf "$file"
.沒有更簡單的方法嗎?
不,更簡單的方法是錯誤的。
如果您使用
ls -t
並通過管道傳遞給tail
您,您將中斷文件名中帶有換行符的文件。如果您rm $(anything)
在名稱中帶有空格的文件將導致損壞。如果您rm "$(anything)"
在名稱中帶有尾隨換行符的文件將導致損壞。也許在特定情況下,您肯定知道更簡單的方法就足夠了,但是如果您可以避免這樣做,您永遠不應該將這樣的假設寫入腳本。
編輯
#!/usr/bin/env bash dir="$1" min_dirs=3 [[ $(find "$dir" -maxdepth 1 -type d | wc -l) -ge $min_dirs ]] && IFS= read -r -d $'\0' line < <(find "$dir" -maxdepth 1 -printf '%T@ %p\0' 2>/dev/null | sort -z -n) file="${line#* }" ls -lLd "$file"
該問題的更完整解決方案,因為它首先檢查目錄計數。