簡體   English   中英

刪除 bash 腳本中除最新的 3 個文件之外的所有文件

[英]Delete all files except the newest 3 in bash script

問題:如何刪除目錄中除最新的 3 個之外的所有文件?

查找最新的 3 個文件很簡單:

ls -t | head -3

但我需要找到除最新的 3 個文件之外的所有文件。 我該怎么做,以及如何刪除同一行中的這些文件而不需要為此進行循環?

為此,我正在使用 Debian Wheezy 和 bash 腳本。

這將列出除最新的三個文件之外的所有文件:

ls -t | tail -n +4

這將刪除這些文件:

ls -t | tail -n +4 | xargs rm --

這還將列出點文件:

ls -At | tail -n +4

並用點文件刪除:

ls -At | tail -n +4 | xargs rm --

但請注意:當文件名包含有趣的字符(如換行符或空格)時,解析ls可能會很危險。 如果您確定您的文件名不包含有趣的字符,那么解析ls是非常安全的,如果它是一次性腳本,則更是如此。

如果您正在開發重復使用的腳本,那么您肯定不應該解析ls的輸出並使用此處描述的方法: http : //mywiki.wooledge.org/ParsingLs

沒有“ls”問題的解決方案(奇怪的命名文件)

這是 ceving 和 anubhava 的答案的結合。 這兩種解決方案都不適合我。 因為我正在尋找一個應該每天運行的腳本來備份存檔中的文件,所以我想避免ls出現問題(有人可以在我的備份文件夾中保存一些有趣的命名文件)。 所以我修改了提到的解決方案以滿足我的需求。

我的解決方案刪除所有文件,三個最新文件除外

find . -type f -printf '%T@\t%p\n' |
sort -t $'\t' -g | 
head -n -3 | 
cut -d $'\t' -f 2- |
xargs rm

一些解釋:

find列出當前文件夾中的所有文件(不是目錄)。 它們與時間戳一起打印出來。
sort根據時間戳(最舊的在頂部)對行sort排序。
head打印出最上面的行,直到最后 3 行。
cut刪除時間戳。
xargs為每個選定的文件運行rm

供您驗證我的解決方案:

(
touch -d "6 days ago" test_6_days_old
touch -d "7 days ago" test_7_days_old
touch -d "8 days ago" test_8_days_old
touch -d "9 days ago" test_9_days_old
touch -d "10 days ago" test_10_days_old
)

這將在當前文件夾中創建 5 個具有不同時間戳的文件。 先運行這個腳本,然后運行刪除舊文件的代碼。

以下看起來有點復雜,但非常謹慎,即使是不尋常的或故意惡意的文件名也是如此。 不幸的是,它需要 GNU 工具:

count=0
while IFS= read -r -d ' ' && IFS= read -r -d '' filename; do
  (( ++count > 3 )) && printf '%s\0' "$filename"
done < <(find . -maxdepth 1 -type f -printf '%T@ %P\0' | sort -g -z) \
     | xargs -0 rm -f --

解釋這是如何工作的:

  • Find 為當前目錄中的每個文件發出<mtime> <filename><NUL>
  • sort -g -z根據第一列(時間)與由 NUL 分隔的行進行一般(浮點數,而不是整數)數字排序。
  • while循環中的第一次read去除 mtime( sort完成后不再需要)。
  • while循環中的第二次read讀取文件名(運行直到 NUL)。
  • 循環遞增,然后檢查一個計數器; 如果計數器的狀態表明我們已經超過了最初的跳過,那么我們打印文件名,以 NUL 分隔。
  • xargs -0然后將該文件名附加到它正在收集以調用rm的 argv 列表中。
ls -t | tail -n +4 | xargs -I {} rm {}

如果你想要一個 1 班輪

在 zsh 中:

rm /files/to/delete/*(Om[1,-4])

如果要包含dotfiles ,請將括號中的部分替換為(Om[1,-4]D)

我認為這適用於文件名中的任意字符(只是用換行符檢查)。

說明:括號包含 Glob 限定符。 O表示“排序,降序”, m表示 mtime(有關其他排序鍵,請參閱man zshexpn - 大型聯機幫助頁;搜索“被排序”)。 [1,-4]僅返回基於一的索引 1 到 (last + 1 - 4) 的匹配項(注意-4表示刪除除 3 之外的所有項)。

ls -t | tail -n +4 | xargs -I {} rm {}

Michael Ballent 的回答最有效

ls -t | tail -n +4 | xargs rm --

如果我的文件少於 3 個,則向我拋出錯誤

不要使用ls -t因為它對於可能包含空格或特殊 glob 字符的文件名是不安全的。

您可以使用所有基於gnu的實用程序來刪除當前目錄中除 3 個最新文件之外的所有文件:

find . -maxdepth 1 -type f -printf '%T@\t%p\0' |
sort -z -nrk1 |
tail -z -n +4 |
cut -z -f2- |
xargs -0 rm -f --

具有任意數量的文件的遞歸腳本以保留每個目錄

還處理帶有空格、換行符和其他奇數字符的文件/目錄

#!/bin/bash
if (( $# != 2 )); then
  echo "Usage: $0 </path/to/top-level/dir> <num files to keep per dir>"
  exit
fi

while IFS= read -r -d $'\0' dir; do
  # Find the nth oldest file
  nthOldest=$(find "$dir" -maxdepth 1 -type f -printf '%T@\0%p\n' | sort -t '\0' -rg \
    | awk -F '\0' -v num="$2" 'NR==num+1{print $2}')

  if [[ -f "$nthOldest" ]]; then
    find "$dir" -maxdepth 1 -type f ! -newer "$nthOldest" -exec rm {} +
  fi
done < <(find "$1" -type d -print0)

概念證明

$ tree test/
test/
├── sub1
│   ├── sub1_0_days_old.txt
│   ├── sub1_1_days_old.txt
│   ├── sub1_2_days_old.txt
│   ├── sub1_3_days_old.txt
│   └── sub1\ 4\ days\ old\ with\ spaces.txt
├── sub2\ with\ spaces
│   ├── sub2_0_days_old.txt
│   ├── sub2_1_days_old.txt
│   ├── sub2_2_days_old.txt
│   └── sub2\ 3\ days\ old\ with\ spaces.txt
└── tld_0_days_old.txt

2 directories, 10 files
$ ./keepNewest.sh test/ 2
$ tree test/
test/
├── sub1
│   ├── sub1_0_days_old.txt
│   └── sub1_1_days_old.txt
├── sub2\ with\ spaces
│   ├── sub2_0_days_old.txt
│   └── sub2_1_days_old.txt
└── tld_0_days_old.txt

2 directories, 5 files

這使用find而不是lsSchwartzian 變換

find . -type f -printf '%T@\t%p\n' |
sort -t $'\t' -g |
tail -3 |
cut -d $'\t' -f 2-

find搜索文件並用時間戳裝飾它們,並使用制表符將兩個值分開。 sort通過制表符拆分輸入並執行通用數字排序,從而正確對浮點數進行排序。 tail應該很明顯, cut裝飾的東西。

裝飾的問題通常是找到一個合適的分隔符,它不是輸入文件名的一部分。 答案使用 NULL 字符。

作為flohall 答案的擴展 如果要刪除除最新的三個文件夾之外的所有文件夾,請使用以下命令:

find . -maxdepth 1 -mindepth 1 -type d -printf '%T@\t%p\n' |
 sort -t $'\t' -g | 
 head -n -3 | 
 cut -d $'\t' -f 2- |
 xargs rm -rf

-mindepth 1將忽略父文件夾和-maxdepth 1子文件夾。

以下對我有用:(干杯🍾)

rm -rf $(ll -t | tail -n +5 | awk '{ print $9}')

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM