当文件超过7个时,删除目录中最旧的文件?

我必须创建一个MySQL数据库的备份脚本(bash)。 当我执行脚本时,将在“/ home / user / Backup”中创建一个sql文件。 问题是,如果“… / Backup”中有超过7个文件,我还必须制作一个删除最旧文件的脚本。 有人知道怎么做吗? 我尝试了所有的东西,但它每次都失败了,计算目录中的文件并检测最老的文件……

介绍

让我们回顾一下问题:任务是检查特定目录中的文件数是否超过一定数量,并删除其中最旧的文件。 起初看起来我们需要遍历目录树一旦计算文件,然后再次遍历它以查找所有文件的最后修改时间,对它们进行排序,并提取最旧的文件以进行删除。 但考虑到在这个特殊情况下OP提到删除文件当且仅当文件数量超过7时,它表明我们可以简单地获取所有文件的列表及其时间戳一次,并将它们存储到变量中。

这种方法的问题是与文件名相关的危险。 正如评论中提到的那样,从不建议解析ls命令,因为输出可以包含特殊字符并破坏脚本。 但是,正如你们中的一些人所知,在类Unix系统(以及Ubuntu)中,每个文件都有与之关联的inode编号。 因此,创建一个带有时间戳的条目列表(以秒为单位进行简单的数字排序)加上由换行符分隔的inode号将保证我们安全地解析文件名。 删除最早的文件名也可以这样做。

下面给出的脚本完全如上所述。

脚本

重要提示 :请阅读评论,特别是在delete_oldest函数中。

 #!/bin/bash # Uncomment line below for debugging #set -xv delete_oldest(){ # reads a line from stdin, extracts file inode number # and deletes file to which inode belongs # !!! VERY IMPORTANT !!! # The actual command to delete file is commented out. # Once you verify correct execution, feel free to remove # leading # to uncomment it read timestamp file_inode find "$directory" -type f -inum "$file_inode" -printf "Deleted %f\n" # find "$directory" -type f -inum "$file_inode" -printf "Deleted %f\n" -delete } get_files(){ # Wrapper function around get files. Ensures we're working # with files and only on one specific level of directory tree find "$directory" -maxdepth 1 -type f -printf "%Ts\t%i\n" } filecount_above_limit(){ # This function counts number of files obtained # by get_files function. Returns true if file # count is greater than what user specified as max # value num_files=$(wc -l <<< "$file_inodes" ) if [ $num_files -gt "$max_files" ]; then return 0 else return 1 fi } exit_error(){ # Print error string and quit printf ">>> Error: %s\n" "$1" > /dev/stderr exit 1 } main(){ # Entry point of the program. local directory=$2 local max_files=$1 # If directory is not given if [ "x$directory" == "x" ]; then directory="." fi # check arguments for errors [ $# -lt 1 ] && exit_error "Must at least have max number of files" printf "%d" $max_files &>/dev/null || exit_error "Argument 1 not numeric" readlink -e "$directory" || exit_error "Argument 2, path doesn't exist" # This is where actual work is being done # We traverse directory once, store files into variable. # If number of lines (representing file count) in that variable # is above max value, we sort numerically the inodes and pass them # to delete_oldest, which removes topmost entry from the sorted list # of lines. local file_inodes=$(get_files) if filecount_above_limit then printf "@@@ File count in %s is above %d." "$directory" $max_files printf "Will delete oldest\n" sort -k1 -n <<< "$file_inodes" | delete_oldest else printf "@@@ File count in %s is below %d." "$directory" $max_files printf "Exiting normally" fi } main "$@" 

用法示例

 $ ./delete_oldest.sh 7 ~/bin/testdir /home/xieerqi/bin/testdir @@@ File count in /home/xieerqi/bin/testdir is below 7.Exiting normally $ ./delete_oldest.sh 7 ~/bin /home/xieerqi/bin @@@ File count in /home/xieerqi/bin is above 7.Will delete oldest Deleted typescript 

补充讨论

这可能很可怕。 。 而且冗长。 。 看起来它做得太多了。 它可能是。 实际上,所有内容都可以推送到一行命令行(在聊天中发布的一个非常修改的muru建议版本处理文件名。为了演示目的,使用echo而不是rm ):

 find /home/xieerqi/bin/testdir/ -maxdepth 1 -type f -printf "%T@ %p\0" | sort -nz | { f=$(awk 'BEGIN{RS=" "}NR==2{print;next}' ); echo "$f" ; } 

但是,我有几个我不喜欢的事情:

  • 它无条件地删除最旧的文件,而不检查目录中的文件数
  • 它直接处理文件名(这要求我使用awkward awk命令,它可能会破坏有空格的文件名)
  • 管道太多(管道太多)

因此,虽然我的脚本对于简单的任务看起来非常巨大,但它会进行更多的检查并且旨在解决复杂文件名的问题。 在Perl或Python中实现它可能会更短,更惯用(我绝对可以做到,我只是选择了bash来解决这个问题)。

我认为@Serg的答案很好,我正在向他和@muru学习。 我做了这个答案,因为我想探索并学习如何根据find的输出创建一个shellscript文件,并使用’ -print根据文件创建/修改的时间对文件进行排序。 请提出改进​​和错误修正(如有必要)。

正如您将注意到的,编程风格非常不同。 我们可以在linux中以多种方式做事:-)

我制作了一个bash shell脚本来匹配OP,@ beginner27_的要求,但是为了其他但类似的目的修改它并不太难。

以下屏幕截图显示了它的测试方式:创建了11个文件,并运行脚本(位于〜/ bin并具有执行权限)。 我从行中删除了#字符

 # bash "$cmd" 

做到这一点

 bash "$cmd" 

脚本第一次发现并打印了11个文件,七个最新的蓝色背景文件和四个最旧的红色背景文件。 删除了四个最旧的文件。 该脚本第二次运行(仅用于演示)。 它发现并打印剩余的七个文件,并且满意,“没有要删除的备份文件”。

在此处输入图像描述

关键的find命令,根据时间对文件进行排序,如下所示,

 find "$bupdir" -type f -printf "%T+ %p\0"|sort -nrz > "$srtlst" 

这是脚本文件。 我将它保存在名为rm_old_backups ~/bin ,但您可以给它任何名称,只要它不会干扰某个已存在的可执行程序名称。

 #!/bin/bash keep=7 # set the number of files to keep # variables and temporary files inversvid="\0033[7m" resetvid="\0033[0m" redback="\0033[1;37;41m" greenback="\0033[1;37;42m" blueback="\0033[1;37;44m" bupdir="$HOME/Backup" cmd=$(mktemp) srtlst=$(mktemp) rmlist=$(mktemp) # output to the screen echo -e "$inversvid$0: keep $keep backup files, remove the oldest files, if more than $keep are found $resetvid" echo "Security fix: You must edit this script and remove the # character from a line near the end of the script '# bash \"\$cmd\"' --> 'bash \"\$cmd\"' otherwise the script will only show what it can do. Please test that it works correctly before you remove that # character!" # the crucial find command, that sorts the files according to time find "$bupdir" -type f -printf "%T+ %p\0"|sort -nrz > "$srtlst" # more output echo -e "${inversvid}time-stamp file-name $resetvid" echo -en "$blueback" sed -nz -e 1,"$keep"p "$srtlst" | tr '\0' '\n' echo -en "$resetvid" echo -en "$redback" sed -z -e 1,"$keep"d "$srtlst" | tr '\0' '\n' | tee "$rmlist" echo -en "$resetvid" # remove oldest files if more files than specified are found if test -s "$rmlist" then echo rm '"'$(sed -z -e 1,"$keep"d -e 's/[^ ]* //' -e 's/$/" "/' "$srtlst")'"'\ | sed 's/" ""/"/' > "$cmd" cat "$cmd" # uncomment the following line to really remove files # bash "$cmd" echo "The oldest backup files are removed" else echo "There is no old backup file to remove" fi # remove temporary files rm $cmd $srtlst $rmlist