尝试删除子目录中除最近2个文件以外的所有文件

3
我正在创建一个定时任务,用于清除指定文件夹下的子目录(仅限第一层)中除最近两个文件以外的所有文件,但是遇到了问题。
以下是我的尝试:
find ./ -type d -exec rm -f $(ls -1t ./ | tail -n +4);
find . -maxdepth 2 -type f -printf '%T@ %p\0' | sort -r -z -n | awk 'BEGIN { RS="\0"; ORS="\0"; FS="" } NR > 5 { sub("^[0-9]*(.[0-9]*)? ", ""); print }' | xargs -0 rm -f

我也尝试创建一个文件数组,的确可以通过总数减2,但是这个数组并没有填充所有文件:

while read -rd ''; do      x+=("${REPLY#* }");  done < <(find . -maxdepth 2 -printf '%T@ %p\0' | sort -r -z -n )

请问有人能帮助我并解释一下他们是怎么做的吗?


1
你尝试过 ls -1t ./ | tail -n +4,在这种方法中你遇到了什么问题? - abasu
@abasu 我被警告不要使用ls命令来列出文件。显然这样做是不安全的?不确定原因。 - gazzwi86
如果您的文件名包含空格,则输出可能不可靠。但我认为这样做日常工作时过于详细。我的意思是,当我逐步思考自己想要做什么时,“ls -trd */|head --lines=2|xargs rm -rf”似乎很自然而然,而不是使用一些花哨的方法。 - abasu
1
@abasu,关于为什么“ls”不安全的完整解释请参见http://mywiki.wooledge.org/ParsingLs。至于“日常工作足够好”,一旦您遇到由于坏文件名(或者更确切地说是由于对文件名不安全处理)而导致的第一次数据丢失事件,您可能会重新考虑这个问题。 - Charles Duffy
3个回答

12

这将列出除最近的两个文件之外的所有文件:

find -type f -printf '%T@ %P\n' | sort -n | cut -d' ' -f2- | head -n -2 

说明:

  • -type f 仅列出文件
  • -printf '%C@ %P\n'
    • %T@ 显示文件的最后修改时间(从1970年开始算起),以秒为单位。
    • %P 显示文件名
  • | sort -n 进行数字排序
  • | cut -d' ' -f2- 从输出中去掉秒,只保留文件名
  • | head -n -2 显示除了最后两行之外的所有内容

因此,要删除所有这些文件,只需通过管道将其附加到 xargs rm 或者 xargs rm -f 即可:

find -type f -printf '%T@ %P\n' | sort -n | cut -d' ' -f2- | head -n -2 | xargs rm

太棒了,谢谢!我稍微修改了查找命令,添加了“-not -name '*.php'”以指定不删除php文件,但它完美地工作。 - gazzwi86
这个程序不能安全地处理包含换行符的文件名。 - Charles Duffy
点赞,这是一个非常棒且直接的回答,感谢分享你的知识 :) - Ma'moon Al-Akash

4
与现有答案不同,这个答案使用NUL作为分隔符来输出find的结果,因此适用于包含任何合法字符(包括换行符)的文件名。
delete_all_but_last() {
  local count=$1
  local dir=${2:-.}
  [[ $dir = -* ]] && dir=./$dir
  while IFS='' read -r -d '' entry; do
    if ((--count < 0)); then
      filename=${entry#*$'\t'}
      rm -- "$filename"
    fi
  done < <(find "$dir" \
             -mindepth 1 \
             -maxdepth 1 \
             -type f \
             -printf '%T@\t%P\0' \
           | sort -rnz)
}

# example uses:
delete_all_but_last 5
delete_all_but_last 10 /tmp

注意,这需要 GNU find 和 GNU sort。 (现有的答案也需要 GNU find)。

不错的解决方案。[[ $dir = -* ]] && dir=./$dir 这一行是做什么用的? - ammonkc
@ammonkc会在任何以破折号开头的目录名称前添加./,以防止它们被传递给任何命令时被解析为选项。 --标志可以在其可用于使用的位置参数后面防止发生这种情况,但在find上下文中,其语义比通常更加复杂。 - Charles Duffy

-1

我刚遇到了同样的问题,这是我解决它的方法:

#!/bin/bash

# you need to give full path to directory in which you have subdirectories
dir=`find ~/zzz/ -mindepth 1 -maxdepth 1 -type d`

for x in $dir; do
        cd $x
        ls -t |tail -n +3 | xargs rm --
done

解释:

  • 使用 tail -n +number 命令,您可以决定在子目录中保留多少个文件。

这种做法不安全的原因与之前的回答类似。此外,将文件列表存储在变量中会导致无法处理某些类型的文件名;因此,“dir”不仅是多余的临时变量,而且是另一个严重的错误。 - tripleee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接