Bash

如何刪除給定目錄中最舊的目錄?

  • January 12, 2012

可能重複:

用於移動最舊文件的 Shell 腳本?

我有一個備份目錄,其中儲存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"

該問題的更完整解決方案,因為它首先檢查目錄計數。

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