我必须创建一个 MySQL 数据库的备份脚本 (bash)。当我执行脚本时,将在“/home/user/Backup”中创建一个 sql 文件。问题是,如果“.../Backup”中有超过 7 个文件,我还必须制作一个脚本来删除最旧的文件。有人知道怎么做吗?我尝试了所有方法,但每次都无法计算目录中的文件并检测到最旧的文件...
让我们回顾一下这个问题:任务是检查特定目录中的文件数量是否超过一定数量,并删除其中最旧的文件。乍一看,我们需要遍历目录树一次统计文件,然后再次遍历找到所有文件的最后修改时间,对它们进行排序,并提取最旧的进行删除。但考虑到在这种特殊情况下,OP提到当且仅当文件数量超过7时才删除文件,这表明我们可以简单地获取所有文件及其时间戳的列表一次,并将它们存储到一个变量中。
这种方法的问题是与文件名相关的危险。正如评论中所提到的,永远不建议解析ls命令,因为输出可能包含特殊字符并破坏脚本。但是,正如你们中的一些人可能知道的那样,在类 Unix 系统(以及 Ubuntu)中,每个文件都有与之关联的索引节点号。因此,创建一个带有时间戳的条目列表(以秒为单位,以便于数字排序)加上由换行符分隔的索引节点号将保证我们安全地解析文件名。删除最旧的文件名也可以通过这种方式完成。
下面提供的脚本与上面描述的完全一样。
重要提示:请阅读评论,尤其是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 "$@"
Run Code Online (Sandbox Code Playgroud)
$ ./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
Run Code Online (Sandbox Code Playgroud)
这可能很可怕。。.而且冗长。。.而且看起来它做得太多了。也可能是这样。事实上,所有内容都可以推到一行命令行上(聊天中发布的处理文件名的 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" ; }
Run Code Online (Sandbox Code Playgroud)
然而,我对此有几点不喜欢的地方:
awk命令,这可能会破坏带有空格的文件名)因此,虽然我的脚本对于简单的任务来说看起来非常巨大,但它会进行更多的检查,旨在解决复杂文件名的问题。用 Perl 或 Python 实现可能会更短、更惯用(我绝对可以做到,我只是碰巧选择了bash这个问题)。
我认为@Serg 的回答很好,我正在向他和@muru 学习。find我做出这个答案是因为我想探索和学习如何根据“操作”的输出创建 shellscript 文件,-print以根据文件的创建/修改时间对文件进行排序。请提出改进和错误修复建议(如有必要)。
您会注意到,编程风格非常不同。我们可以在 Linux 中以多种方式做事:-)
我制作了一个 bash shell 脚本来满足 OP @beginner27_ 的要求,但为了其他但类似的目的修改它并不太困难。
下面的屏幕截图显示了它是如何测试的:创建了 11 个文件,并且运行了脚本(位于 ~/bin 中并具有执行权限)。我已从该行中删除了 # 字符
# bash "$cmd"
Run Code Online (Sandbox Code Playgroud)
做到这一点
bash "$cmd"
Run Code Online (Sandbox Code Playgroud)
脚本第一次发现并打印 11 个文件,其中 7 个最新文件具有蓝色背景,4 个最旧文件具有红色背景。四个最旧的文件将被删除。该脚本第二次运行(仅用于演示)。它发现并打印剩余的七个文件,并满意地回答“没有要删除的备份文件”。
find根据时间对文件进行排序的关键命令如下所示,
find "$bupdir" -type f -printf "%T+ %p\0"|sort -nrz > "$srtlst"
Run Code Online (Sandbox Code Playgroud)
这是脚本文件。我将其保存~/bin为名称rm_old_backups,但您可以给它任何名称,只要它不干扰可执行程序的某些已存在的名称即可。
#!/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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2095 次 |
| 最近记录: |