Shell脚本:计算文件数量,然后删除最旧的文件

60

我刚接触shell脚本编程,需要帮助。 我有一个备份文件目录。如果有超过10个备份文件,我想要删除最旧的文件,以便只留下最新的10个备份文件。

到目前为止,我知道如何计算文件数量,这似乎很容易,但如果文件数超过10,如何删除最旧的文件呢?

if [ls /backups | wc -l > 10]
    then
        echo "More than 10"
fi

这个条件的写法对我来说并不起作用,我必须将第一行更改为if [ $(ls /backups | wc -l) -gt 10 ]。基本上,方括号内的空格很重要,管道命令需要用$()包装,而在方括号内比较数字时使用-gt而不是> - Chris
11个回答

103

试试这个:

ls -t | sed -e '1,10d' | xargs -d '\n' rm

这应该能处理文件名中的所有字符(除了换行符)。

这里发生了什么?

  • ls -t 按修改时间递减顺序列出当前目录中的所有文件。也就是说,最近修改的文件排在前面,每行一个文件名。
  • sed -e '1,10d' 删除前10行,即最新的10个文件。我使用它而不是 tail,因为我总是记不住是需要 tail -n +10 还是 tail -n +11
  • xargs -d '\n' rm 收集每个输入行(不包括结束换行符),并将每行作为参数传递给 rm

像这样的任何操作,请在安全的地方进行实验。


1
哦,我总能最终弄清楚它,但每次都会发现一些非直观的东西。sed -e 1,10d 正是它所说的:删除前10行。 - Dale Hagglund
2
这是一个完美、直截了当的解决方案。谢谢。 - Andrew Ensley
2
这不是要求删除最老的10个吗?而不是最新的10个?使用sed -e '1,10d' .. 是不正确的吗? - carl crott
5
@delinquentme要求留下最新的10个文件,通过从ls命令的输出中删除它们,管道的以下部分会删除所有旧文件。 - Dale Hagglund
3
@DaleHagglund,我刚学到了-r选项,当使用时,仅在存在非空行时才运行xargs。 因此,将该选项添加到xargs中可以消除错误。 - Chris
显示剩余18条评论

30

find 是这种任务中常用的工具:


find ./my_dir -mtime +10 -type f -delete

解释

  • ./my_dir 你的目录(用你自己的替换)
  • -mtime +10 大于10天之前的文件
  • -type f 只有文件
  • -delete 毫不意外。在执行整个命令之前,删除它来测试你的find过滤器

请确保./my_dir存在以避免出现意外情况!


1
我认为这个应该被选为最佳答案,而选择的只是浪费命令!祝好运。 - user3677687
9
关于这个解决方案的几点说明:(1) 如您所说,使用“-mtime 10”将选择删除十天前的文件。但是,OP的问题要求删除最旧的十个文件,而不是所有早于十天的文件。(2) find将遍历整个目录树,在任何级别删除文件。在这里,OP也没有要求此行为。 - Dale Hagglund
3
在之前的评论中,我错误地说OP要求删除最旧的十个文件。实际上,OP要求保留最新的十个文件,并删除所有更旧的文件。很抱歉造成困惑,但无论如何,"-mtime 10"都不能完全满足要求。 - Dale Hagglund
这不是楼主的问题,但它确实解决了我正在寻找的问题! :D 感谢 @MahyarDamavand - Penumbra

14

请确保您的pwd是正确的目录以删除文件,然后(假设文件名中仅有常规字符):

ls -A1t | tail -n +11 | xargs rm

保留最新的10个文件。我结合相机程序“motion”使用它来保留最近的抓拍文件。感谢之前所有的答案,因为你们向我展示了如何实现。


10

这种情况的正确处理方式是使用logrotate


2
logrotate 是一个不错的选择,但它可能有点过于复杂:需要配置文件,并且在某种程度上偏向于半官方的日志文件位置。此外,它是否假定应该先旋转日志(即将 .N 重命名为 .N+1),然后再删除最旧的日志?至少按照写作方式,OP 的问题并没有暗示固定名称的旋转。 - Dale Hagglund

3

我喜欢@Dennis Williamson和@Dale Hagglund的回答。(对每个人+1)

这里是另一种使用find(带有-newer测试)的方法,与您开始的方式类似。

这是在cygwin上的bash中完成的...

if [[ $(ls /backups | wc -l) > 10 ]]
then
  find /backups ! -newer $(ls -t | sed '11!d') -exec rm {} \;
fi

2

简单的文件计数器:

max=12
n=0
ls -1t *.dat |
while read file; do
    n=$((n+1))
    if [[ $n -gt $max ]]; then
        rm -f "$file"
    fi
done

2
我刚找到了这个主题,并从mikecolley的解决方案中得到了第一步的帮助。由于我需要一个单行的homematic(raspberrymatic)脚本解决方案,我遇到了一个问题,这个命令只给了我文件名而不是整个路径,但是"rm"需要整个路径。我的使用的CUxD Exec命令不能在所选文件夹中启动。

因此,这是我的解决方案:

ls -A1t $(find /media/usb0/backup/ -type f -name homematic-raspi*.sbk) | tail -n +11 | xargs rm

解释:

  • find /media/usb0/backup/ -type f -name homematic-raspi*.sbk 在文件夹 /media/usb0/backup/ 中搜索名字类似于 homematic-raspi*.sbk(区分大小写)的文件,使用 -iname(不区分大小写)也可以。
  • ls -A1t $(...) 列出由 find 返回的文件,不包括以 "." 或 ".." 开头的文件 -A,按修改时间排序 -t,只返回一列 -1
  • tail -n +11 只返回最后10行之后的内容,用于接下来的 rm
  • xargs rm 最后删除列表中剩余的文件。

希望这能帮助其他人更快地找到答案并使解决方案更加灵活。


1
在非常有限的chroot环境中,我们只有几个可用程序来完成最初的要求。我们是这样解决的:
MIN_FILES=5
FILE_COUNT=$(ls -l | grep -c ^d )


if [ $MIN_FILES -lt $FILE_COUNT  ]; then
  while [ $MIN_FILES -lt $FILE_COUNT ]; do
    FILE_COUNT=$[$FILE_COUNT-1]
    FILE_TO_DEL=$(ls -t | tail -n1)
    # be careful with this one
    rm -rf "$FILE_TO_DEL"
  done
fi

解释:

  • FILE_COUNT=$(ls -l | grep -c ^d ) 统计当前文件夹中的所有文件数量。我们也可以使用wc -l,但该主机上未安装wc。
  • FILE_COUNT=$[$FILE_COUNT-1] 更新当前的$FILE_COUNT
  • FILE_TO_DEL=$(ls -t | tail -n1) 将最旧的文件名保存在$FILE_TO_DEL变量中。tail -n1返回列表中的最后一个元素。

1
stat -c "%Y %n" * | sort -rn | head -n +10 | \
        cut -d ' ' -f 1 --complement | xargs -d '\n' rm

步骤:获取每个文件的最后修改时间(格式为“时间 文件名”),按从旧到新的顺序排序,保留除最后十个条目外的所有条目,然后保留除第一个字段外的所有内容(仅保留文件名部分)。

编辑:使用cut代替awk,因为后者并不总是可用。

编辑2:现在处理带有空格的文件名。


我通常使用“cut”作为最后一步,因为并非所有机器都安装了awk。 - Jay

0

根据他人的建议和一些awk技巧,我让它工作了。我知道这是一个旧的线程,但我在这里没有找到一个像样的答案,而这个方法对我很有用。这只是删除最旧的文件,但你可以将head -n 1更改为10,以获取最旧的10个文件。

find $DIR -type f -printf '%T+ %p\n' | sort | head -n 1 | awk '{first =$1; $1 =""; print $0}' | xargs -d '\n' rm


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