刪除除最大文件之外的所有文件

刪除除最大文件之外的所有文件

我有一個包含許多子資料夾的資料夾。我想從每個子資料夾中刪除所有較小的文件,只留下最大的文件。

例如:

Subfolder1
---------- File 1 ---- 300k
---------- File 2 ---- 299k
---------- File 3 ---- 800k

應該只file 3保留 800k。如果該資料夾只有一個文件,則保留該文件。

此程式碼有效,但我無法將其放入 for 循環中(用於目錄遞歸):

find . -type f -maxdepth 1 | sort -n -r | tail -n +2 | xargs -I{} rm -v {}

我怎樣才能做到這一點?

答案1

~$ tree -fQFi --sort=size pluto
"pluto"
"pluto/pluto1"/
"pluto/pluto1/pluto3"/
"pluto/pluto1/pluto3/nozero.txt"
"pluto/pluto1/pluto3/zero ed.txt"
"pluto/pluto1/nozero.txt"
"pluto/pluto2"/
"pluto/pluto2/nozero.txt"
"pluto/pluto2/nozer.txt"
"pluto/pluto2/zero.txt"
"pluto/pluto4"/
"pluto/pluto4/zeroed.txt"
"pluto/zeroed.txt"

4 directories, 8 files

~$ tree -fQFic --noreport --sort=size pluto | \
> awk -F"/" 'NR==1||/\/$/{next}; \
>     {path=""; for(i=1;i<NF;i++) path=path$i; if(a[path]++) print}'
"pluto/pluto1/pluto3/zero ed.txt"
"pluto/pluto2/nozer.txt"
"pluto/pluto2/zero.txt"

~$ tree -fQFic --noreport --sort=size pluto | \
> awk -F"/" 'NR==1||/\/$/{next}; \
>     {path=""; for(i=1;i<NF;i++) path=path$i; if(a[path]++) print}' | \
> xargs rm -v
'pluto/pluto1/pluto3/zero ed.txt' rimosso
'pluto/pluto2/nozer.txt' rimosso
'pluto/pluto2/zero.txt' rimosso

~$ tree -fQFi --sort=size pluto
"pluto"
"pluto/pluto1"/
"pluto/pluto1/pluto3"/
"pluto/pluto1/pluto3/nozero.txt"
"pluto/pluto1/nozero.txt"
"pluto/pluto2"/
"pluto/pluto2/nozero.txt"
"pluto/pluto4"/
"pluto/pluto4/zeroed.txt"
"pluto/zeroed.txt"

4 directories, 5 files

tree按目錄列出,然後按大小降序列出。

  • awk的第一行程式碼跳過 的tree輸出第一行或者帶有尾部斜杠的行(即目錄)
  • awk的第二行程式碼從完整路徑(for循環)建立目錄名,然後列印完整路徑名,前提是在前面的行中遇到過一次目錄名(即,從第二個列出的檔案開始,為每個目錄列印)

答案2

理由

這是我嘗試建立一個可以使用的命令任何目錄和檔名。一般來說,Linux 中的路徑(以及檔案系統中的名稱)可以包含 null ( 0x00) 和 之外的任何字元/。麻煩的字元可能是「」(空格)、任何其他白色字元、、、'換行符"、其他不可列印字元。因此,重要的是:

  • 放棄用其他字元替換某些字元的工具(例如,ls將列印的許多實作用?於不可列印);
  • 將所有名稱作為空終止字串傳遞(選擇可以解析它們的工具);
  • 正確引用。

我受到以下討論的啟發這個另一個答案


實際命令

測試版本,它只會刪除ls要刪除的檔案:

find -type d -exec sh -c 'find "$0" -maxdepth 1 -mindepth 1 -type f -exec stat --printf "%s %n\0" \{\} + | sort -znr | tail -zn +2' {} \; | cut -zf 2- -d " " | xargs -0r ls -l

ls是的,儘管我剛才說了這些,但我還是在這裡使用。這是因為ls輸出沒有被進一步解析。我只是用它來顯示結果。如果您碰巧有目錄或文件的名稱中含有麻煩的字符,那麼您將觀察到其行為,ls這應該會說服您從不解析ls(除非你知道你絕對安全)。儘管如此,麻煩的名字還是會一路過去ls,這就是重點。

了解測試版本(請參閱下面的一些解釋)並在讓工作版本之前嘗試一下(略低於)刪除你的檔案。請記住,我只是網路上的一個隨機人。

工作版本,它將刪除您的檔案:

find -type d -exec sh -c 'find "$0" -maxdepth 1 -mindepth 1 -type f -exec stat --printf "%s %n\0" \{\} + | sort -znr | tail -zn +2' {} \; | cut -zf 2- -d " " | xargs -0r rm

解釋

這是分成多行的測試版本(儘管它仍然是一行bash;注意我使用這個伎倆內嵌評論):

find -type d -exec   `# Find all directories under (and including) the current one.` \
  sh -c '            `# In every directory separately...` \
    find "$0" -maxdepth 1 -mindepth 1 -type f -exec   `# ...find all files,...` \
      stat --printf "%s %n\0" \{\} + |   # ...get their sizes and names,...
    sort -znr |                          # ...sort by size...
    tail -zn +2'                        `# ...and discard the "biggest" entry.` \
    {} \
  \; |                                   # (All the directories have been processed).
cut -zf 2- -d " "  |                     # Then extract filenames...
xargs -0r ls -l                          # ...and ls them (rm in the working version).

使用的技術、克服的障礙:

  • 解析字串的工具被告知可以處理以 null 結尾的字串:
    • stat --printf "…\0";
    • sort -z, tail -z, cut -z;
    • xargs -0 …;
    • find -print0(本例中不需要,但一般來說很常見,因此我無論如何都會提到它)。
  • sh -c '…'就是裡面使用管道的方式find -exec
  • find -type d -exec sh -c 'find "{}" …將中斷包含";的目錄名稱find -type d -exec sh -c 'find "$0" … ' {} \;工作正常。
  • {}內部find語句中的轉義 ( \{\}) 以防止外部語句find取代它們。
  • cut可以立即執行tail,它將cut為每個目錄運行一個。將其放置在外層之外,find可以讓一個人一次cut完成所有切割工作。
  • 當沒有輸入時阻止(在工作版本中)運行-r的選項。xargslsrmxargs

相關內容