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

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

我有一个包含许多子文件夹的文件夹。我想从每个子文件夹中删除所有较小的文件,只留下最大的文件。

例如:

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代码的第二行从完整路径构建一个 dirname (for循环),然后打印完整路径名当且仅当 dirname 在前几行中遇到过一次(即,对于每个目录,它从列出的第二个文件开始打印)

答案2

理由

这是我尝试构建一个可以与之配合使用的命令任何目录和文件名。一般来说,Linux 中的路径(以及文件系统中的名称)可能包含除空字符 ( 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).

所用技术及克服障碍:

  • 解析字符串的工具被告知要使用以空字符结尾的字符串:
    • stat --printf "…\0"
    • sort -z,,;tail -zcut -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选项xargs可防止lsrm在工作版本中)在没有输入时运行xargs

相关内容