递归删除最旧的文件,直到达到最大限制

递归删除最旧的文件,直到达到最大限制

我正在尝试修改Sergiy 的剧本以递归方式执行(目前它只删除 1 个文件,但我希望它删除所有文件,直到达到最大文件限制)。但我搞不懂。我觉得我应该能够修改第一个函数(“删除最旧的”)以读取多个时间戳并将其传递给下一行,但我不熟悉这个命令,找不到任何相关信息。任何想法都将不胜感激!

答案1

如果希望脚本仍然只对一个目录进行操作,而不对子目录进行递归,则可以循环进行计数和删除while。函数的最后一部分main应改为如下所示:

local file_inodes=$(get_files)
    while filecount_above_limit 
    do
        printf  "@@@ File count in %s is above %d." "$directory" $max_files
        printf "Will delete oldest\n"
        sort -k1 -n <<< "$file_inodes" | delete_oldest
        local file_inodes=$(get_files)
    done
    printf "@@@ File count in %s is below %d."  "$directory" $max_files
    printf "Exiting normally"

警告!
这个简单改动的问题在于,如果你没有在顶部的删除行中添加注释,脚本将无限循环,因为它会在每次删除后重新计算文件数量。如果没有删除文件,文件数量将保持不变,循环永远不会退出。

可以以更复杂的方式修改脚本,file_inodes一旦删除文件 inode,就从数组中删除它,并负向增加file_count变量,而不是重复该local file_inodes=$(get_files)行。这将处理非删除检查的情况,但我将把这个留给别人去做。

答案2

我建议使用另一种解决方案,即在目标目录树结构中递归遍历并删除所有文件,但保留预定义的一定数量的新文件。该解决方案基于:(1)递归 bash 脚本以及(2)解释 shell 脚本以递归方式打印完整目录树

1.创建可执行脚本文件,名为walkr走走),位于/usr/local/bin可作为 shell 命令访问 (更详细的步骤)。

2.脚本的内容walkr非常简单:

#!/bin/bash

[[ -z "${NFK}" ]] && NFK='7' || NFK="$NFK"
[[ -z "${1}" ]] && ABS_PATH="${PWD}" || cd "${1}" && ABS_PATH="${PWD}"

file_operations() {
        local IFS=$'\t\n' # Change the value of the Internal Field Separator locally
        rm $(ls -lt | grep -Po '^-.*[0-9]{2}:[0-9]{2} \K.*' | tail -n +"$((NFK+1))") 2>/dev/null
}

walk() {
        cd "$1" && file_operations # Change directory to the destination path and call the above function
        for item in "$1"/*; do [[ -d "$item" ]] && walk "$item"; done # Make the recursion
}

walk "${ABS_PATH}"

3.解释:

  • 一开始,脚本会检查变量$NFK(确定要保留的文件数量)是否已预先设置 - 条件[[ -z "${NFK}" ]]。如果没有设置,则默认值为7

  • 接下来,脚本处理目标路径(命令的标准输入)。如果没有提供目标路径(条件[[ -z "${1}" ]]),脚本将进入当前目录。

  • walk()最后将执行主要函数。


  • 功能walk()

    • 首先,它会将目录更改为目标路径cd "$1",然后调用file_operations()内部运行的函数。

    • 此外,对于$item当前目录"$1"/*(也是目录)中的每个,[[ -d "$item" ]]该函数walk()将再次执行,因此我们创建了递归

  • 功能file_operations()

    • 最初它将设置内部 Bash 变量 $IFS因此我们可以正确处理<the list of the files to be removed>,无论单独的文件名里面是否有空格。

    • 随后将执行命令rm $(<the list of the files to be removed>)。错误重定向2>/dev/null适用于没有内容可删除的情况。<the list of the files to be removed>方法如下:

      • 该命令ls -lt将以长列表格式列出当前目录的内容-l,并按修改时间排序,最新内容排在第一位-t。并且此列表通过管道传输|到下一个命令。

      • 下一个命令grep -Po '^-.*[0-9]{2}:[0-9]{2} \K.*'将裁剪这些^-开头的行,从开头到模式[0-9]{2}:[0-9]{2}_-P 。带有选项的选项-o将输出与模式匹配的字符串^-.*[0-9]{2}:[0-9]{2}_\K通知将忽略其之前的匹配部分。(来源-这个有用的答案

        因此,我们将仅从列表中获取文件的名称。在输出中,ls -l描述目录的行以 开头d,而描述文件的行以 开头-。(想法的来源

        此模式与时间格式匹配00:00

      • 最后命令tail -n +"$((NFK+1))将切断前几行我们的文件列表。这些文件的数量前几行等于$NFK加1的值,这是命令的要求tail

4.使用示例:

  • walkr针对当前目录运行:

    walkr      # You shouldn't use any argument, 
    walkr ./   # but you can use also this format
    
  • walkr对任何子目录运行:

    walkr <directory name>
    walkr ./<directory name>
    walkr <directory name>/<sub directory>
    
  • walkr要对任何其他目录运行:

    walkr /full/path/to/<directory name>
    
  • 要更改要保留的文件数量(例如3),请使用此格式

    NFK=3 walkr
    NFK=3 walkr /full/path/to/<directory name>
    # etc.
    

5.让我们来玩一下脚本walkr

  • 我们可以使用命令touch file.name -d "1 hour ago"创建一个空文件,日期为一小时前。因此,我们可以使用以下命令来创建如此处所示的目录结构

    mkdir -p ~/temp/dir{A..C} && cd ~/temp ;\
    DEST='';      touch ${DEST}new_file{1..7} && touch ${DEST}older_file{1..7} -d "1 hour ago" && touch ${DEST}oldest_file{1..7} -d "2 hour ago" ;\
    DEST='dirA/'; touch ${DEST}new_file{1..7} && touch ${DEST}older_file{1..7} -d "1 hour ago" && touch ${DEST}oldest_file{1..7} -d "2 hour ago" ;\
    DEST='dirB/'; touch ${DEST}new_file{1..7} && touch ${DEST}older_file{1..7} -d "1 hour ago" && touch ${DEST}oldest_file{1..7} -d "2 hour ago" ;\
    DEST='dirC/'; touch ${DEST}new_file{1..7} && touch ${DEST}older_file{1..7} -d "1 hour ago" && touch ${DEST}oldest_file{1..7} -d "2 hour ago"
    
  • 现在我们可以进行一些测试:

    在此处输入图片描述


更新脚本的功能。以下是上述脚本的更新版本:

#!/bin/bash

[[ -z "${1}" ]] && ABS_PATH="${PWD}" || cd "$1" && ABS_PATH="${PWD}"
[[ -z "${2}" ]] && NFK='7' || NFK="$2"  # Number of the files to be kept
[[ -z "${3}" ]] && REC='1' || REC="$3"  # REC='1' - work recursively
[[ -z "${4}" ]] && VRB='1' || VRB="$4"  # VRB='1' - work in verbose mode

file_operations() {
        local IFS=$'\t\n' # Change the value of the Internal Field Separator locally

        if [ "$VRB" == "1" ]
        then # Verbose mode:
                rm -v $(ls -lt | grep -Po '^-.*[0-9]{2}:[0-9]{2} \K.*' | tail -n +"$((NFK+1))") 2>/dev/null && printf " -from: '%s' \n" "$1" || echo "nothing to remove in: '$1'"
        else # Quiet mode:
                rm $(ls -lt | grep -Po '^-.*[0-9]{2}:[0-9]{2} \K.*' | tail -n +"$((NFK+1))") 2>/dev/null
        fi
}

walk() {
        # Change directory to the destination path and call the above function, pass $1 for the verbose mode
        cd "$1" && file_operations "$1"
        # If REC='1': Recursive mode -- Make the recursion; otherwise work on the curent level
        if [ "$REC" == "1" ]; then for item in "$1"/*; do [[ -d "$item" ]] && walk "$item"; done; fi
}

walk "${ABS_PATH}"
  • 此版本的脚本可以处理更多输入变量。它具有安静的冗长模式并且可以非递归地工作。

  • 完整格式为:

    walkr '<destination path>' '<number of lines to be kept>' '<no recursion>' '<quiet>'
    

    <no recursion>和的确切内容<quiet>无关紧要。只要输入变量$3$4不能为空,就会覆盖默认行为。

相关内容