【问题标题】:Shell script to count files, then remove oldest files用于计数文件的 Shell 脚本,然后删除最旧的文件
【发布时间】:2020-01-21 20:07:21
【问题描述】:

我是 shell 脚本的新手,所以我需要一些帮助。我有一个充满备份的目录。如果我有超过 10 个备份文件,我想删除最旧的文件,以便仅剩下 10 个最新的备份文件。

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

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

【问题讨论】:

  • 所写的这个条件对我不起作用。我不得不将第一行更改为:if [ $(ls /backups | wc -l) -gt 10 ] 基本上,括号内的空格很重要,管道命令需要包裹在$() 中,并且使用-gt 而不是> 来比较数字括号

标签: linux bash shell


【解决方案1】:

试试这个:

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

与任何此类事情一样,请在安全的地方进行实验。

【讨论】:

  • 哦,我总能最终弄清楚,我只是每次都发现一些不直观的东西。 sed -e 1,10d 就像它所说的那样:删除前 10 行。
  • 完美、直接地回答了这个问题。谢谢。
  • 这不是要求删除 10 个最旧的……而不是 10 个最新的吗?制作 sed -e '1,10d' .. 不正确?
  • @delinquentme OP 要求留下 10 个最新文件,并通过从 ls 的输出中删除它们,管道的以下部分删除所有旧文件。
  • @DaleHagglund,我刚刚了解了-r 选项,该选项在使用时仅在有任何非空行时运行 xargs。所以将该选项添加到xargs 可以消除错误
【解决方案2】:

find 是此类任务的常用工具:

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

解释

  • ./my_dir你的目录(替换为你自己的)
  • -mtime +10 10 天以上
  • -type f 仅限文件
  • -delete 毫不奇怪。 在执行整个命令之前删除它以测试您的 find 过滤器

并注意./my_dir 的存在以避免意外!

【讨论】:

  • 我认为这应该被检查为最佳答案,而选择的只是浪费命令!祝你好运
  • 关于此解决方案的几点说明:(1)使用-mtime 10,如您所说,选择十天以上的文件进行删除。但是,OP 的问题要求十个最旧的文件,而不是所有超过十天的文件。 (2)find 将遍历整个目录树,删除任何级别的文件。在这里,OP 也没有要求这种行为。
  • 在之前的评论中,我错误地说 OP 要求删除十个最旧的文件。事实上,OP 要求保留十个最新文件,并删除所有旧文件。很抱歉造成混淆,但在任何一种情况下,-mtime 10 都不能满足要求。
  • 这不是 OP 的问题,但它确实解决了我正在寻找的问题! :D 谢谢@MahyarDamavand
【解决方案3】:

确保您的 pwd 是删除文件的正确目录(假设文件名中只有常规字符):

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

保留最新的 10 个文件。我将它与相机程序“运动”一起使用以保留最新的帧抓取文件。感谢所有正在进行的答案,因为您向我展示了如何做到这一点。

【讨论】:

    【解决方案4】:

    做这种事情的正确方法是使用logrotate

    【讨论】:

    • logrotate 是一个很好的答案,但它可能有点重量级:它需要一个配置文件,并且至少在某种程度上偏向于日志文件的半官方位置。另外,它不是假设它应该首先轮换日志(即,将.N 重命名为.N+1)然后删除最旧的日志吗?至少按照书面规定,OP 的问题并不意味着固定名称的轮换。
    【解决方案5】:

    我喜欢@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
    

    【讨论】:

      【解决方案6】:

      简单的文件计数器:

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

      【讨论】:

        【解决方案7】:

        我刚刚找到了这个主题,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 仅搜索 -type f 命名为 -name homematic-raspi*.sbk(区分大小写)或在文件夹 /media/usb0/backup/ 中使用 -iname(不区分大小写)的文件
        • ls -A1t $(...) 列出由find 给出的文件,没有以“.”开头的文件或“..”-A 按 mtime 排序 -t 并仅返回一列 -1
        • tail -n +11 仅返回最后 10 -n +11 行,用于关注 rm
        • xargs rm 最后把列表中的raiming文件去掉

        也许这有助于其他人进行更长时间的搜索并使解决方案更加灵活。

        【讨论】:

          【解决方案8】:
          stat -c "%Y %n" * | sort -rn | head -n +10 | \
                  cut -d ' ' -f 1 --complement | xargs -d '\n' rm
          

          细分:获取每个文件的最后修改时间(格式为“timefilename”),将它们从最旧到最新排序,保留除最后十个条目之外的所有条目,然后保留除第一个字段之外的所有条目(只保留文件名部分)。

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

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

          【讨论】:

          • 我通常在最后一步使用 'cut',因为 awk 并不总是安装在所有机器上。
          【解决方案9】:

          在非常有限的 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 ) 计算当前文件夹中的所有文件。除了 grep,我们也可以使用 wc -l,但该主机上没有安装 wc。
          • FILE_COUNT=$[$FILE_COUNT-1] 更新当前的$FILE_COUNT
          • FILE_TO_DEL=$(ls -t | tail -n1) 将最早的文件名保存在 $FILE_TO_DEL 变量中。 tail -n1 返回列表中的最后一个元素。

          【讨论】:

            【解决方案10】:

            根据其他人的建议和一些 awk foo,我得到了这个工作。我知道这是一个旧线程,但我没有在这里找到一个像样的答案,这为我排序。这只是删除最旧的文件,但您可以将 head -n 1 更改为 10 并获取最旧的 10。

            找到 $DIR -type f -printf '%T+ %p\n' |排序 |头-n 1 | awk '{第一个 =$1; $1 =“”;打印 $0}' | xargs -d '\n' rm

            【讨论】:

              【解决方案11】:

              通过 stat & find 命令使用 inode 编号(以避免讨厌的字符在文件名问题):

              stat -f "%m %i" * | sort -rn -k 1,1 | tail -n +11 | cut -d " " -f 2 | \
                 xargs -n 1 -I '{}' find "$(pwd)" -type f -inum '{}' -print
              
              #stat -f "%m %i" * | sort -rn -k 1,1 | tail -n +11 | cut -d " " -f 2 | \
              #   xargs -n 1 -I '{}' find "$(pwd)" -type f -inum '{}' -delete 
              

              【讨论】:

              • 查看 find 的 -exec + 而不是使用 xargs,当然不能一起使用。
              猜你喜欢
              • 2012-06-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2022-07-11
              • 1970-01-01
              • 1970-01-01
              • 2017-11-21
              • 1970-01-01
              相关资源
              最近更新 更多