2014-11-05 111 views
8

問題:如何刪除除最新3之外的目錄中的所有文件?在bash腳本中刪除除最新3以外的所有文件

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

ls -t | head -3 

但我需要找到除了最新的3個文件中的所有文件。我該怎麼做,以及如何在同一行中刪除這些文件,而不必爲循環創建一個循環?

我使用的是Debian Wheezy和bash腳本。

+3

'ls'實際上是工作的錯誤工具 - 請參閱http://mywiki.wooledge.org/ParsingLs。如果你有GNU查找,你可以使用具有時間戳的'-printf'格式的字符串(理想情況是在UNIX時間爲'sort -n -z'),一個分隔符,然後跟隨一個NUL做得更好。這樣即使帶有換行符的文件名也不會丟失。 – 2014-11-05 19:14:36

+0

我也不同意在這裏使用循環是不必要的。做正確而有力的事情與簡潔地做事並不相同,但其他任何事情......好......不正確。 – 2014-11-05 19:17:12

回答

22

這將列出,除了最新的三位全文件:

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

+0

如何在不創建循環的情況下觸發刪除命令(即使用單線程)? – bytecode77 2014-11-05 19:18:54

+0

是的,文件名是系統的。你如何觸發刪除? – bytecode77 2014-11-05 19:24:30

+0

@DevilsChild:取決於你是否在意正確。如果你不在乎,只需管道xargs ......但不要**如果它是重要的(如備份腳本)那樣做。 – 2014-11-05 19:24:43

7

下看起來有點複雜,但非常謹慎是正確的即使使用了不尋常的或有意的惡意文件名。不幸的是,它需要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 '%[email protected] %P\0' | sort -g -z) \ 
    | xargs -0 rm -f -- 

解釋它是如何工作:

  • 查找發出<mtime> <filename><NUL>在當前目錄下的每個文件。
  • sort -g -z根據第一列(時間)用NUL分隔的行做一般(浮點數,而不是整數)數值排序。
  • 第一個readwhile循環中關閉了mtime(在完成sort後不再需要)。
  • while循環中的第二個read讀取文件名(運行直到NUL)。
  • 循環遞增,然後檢查計數器;如果計數器的狀態表明我們已經超過了初始跳過,那麼我們打印由NUL分隔的文件名。
  • xargs -0然後將該文件名追加到它正在收集的argv列表中,以調用rm
+0

你錯過了xargs中的'-0'嗎?另外,你可以使用group和dummy read跳過前三個:'{read;讀;讀;而...完成; } <<(find ...)'這將避免需要一個計數器。 – 2014-11-05 19:25:08

+0

@gniourf_gniourf,是的,但他們需要用'-d'''作爲虛擬閱讀,這使得他們足夠長,我去了櫃檯。 '-0'上的好點;我只在那之前測試過。 – 2014-11-05 19:26:51

+0

足夠公平';)' – 2014-11-05 19:27:07

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

如果你想有一個1個襯墊

+0

這是安全的對有趣的名字文件? – mpen 2017-05-13 22:35:42

+1

如果你的意思是中間有*的名字,我會說不。使用find命令找到它們。 – 2017-05-15 21:49:16

0

這使用find而不是lsSchwartzian transform

find . -type f -printf '%[email protected]\t%p\n' | 
sort -t $'\t' -g | 
tail -3 | 
cut -d $'\t' -f 2- 

find搜索文件,並帶有時間戳裝飾他們,並使用製表兩個值分開。 sort通過製表符分割輸入並執行一般的數字排序,該排序正確地對浮點數進行排序。 tail應該是明顯的,cut undecorates。

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

0

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

您可以使用所有gnu基於工具來刪除所有,但3名最新的文件在當前目錄中做到這一點:

find . -maxdepth 1 -type f -printf '%[email protected]\t%p\0' | 
sort -z -nrk1 | 
tail -z -n +4 | 
cut -z -f2- | 
xargs -0 rm -f -- 
2

這是ceving的和anubhava的回答的組合。 這兩種解決方案都不適合我。因爲我正在尋找一個應該每天運行的腳本來備份檔案中的文件,所以我想避免ls(某人可能在我的備份保存文件夾中保存了一些有趣的名稱文件)的問題。所以我修改了提到的解決方案以適應我的需求。切爾寧的解決方案刪除了​​三個最新的文件 - 不是我需要和被問到的。

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

find . -type f -printf '%[email protected]\t%p\n' | 
sort -t $'\t' -g | 
head -n -3 | 
cut -d $'\t' -f 2- | 
xargs rm 

一些解釋:

find列出當前文件夾中的所有文件(不是目錄)。他們打印出時間戳。
sort根據時間戳排序行(最上面最老)。
head打印出最後一行,最後5行。
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個文件,在當前文件夾不同的時間戳。首先運行此代碼並刪除代碼以測試代碼。

相關問題