Bash 脚本:有条件地删除旧文件,同时保留最新副本

Bash 脚本:有条件地删除旧文件,同时保留最新副本

注意:虽然 jeff-schaller 给出了答案,但这取决于zsh;所以我想得到一个基于的答案Bash

我喜欢创建一个 Bash 脚本来有条件地从备份目录中删除旧文件。

两个不同的文件备份有两个条件:

1、保留 的最新副本Edge_Profile_*.tgz,并删除 的其余部分Edge_Profile_*.tgz 仅限超过 5 天的情况

2、保留 的最新副本Firefox_Profile_*.tgz,并删除 的其余部分Firefox_Profile_*.tgz无论他们多大年纪

以下是我修改此 AskUbuntu 答案的方法:https://askubuntu.com/a/933098/928088

脚本:

#!/bin/bash

LOG_FILE="/home/admn/Cleanup.log"
TEMP_LOG="/tmp/Temp_Cleanup.log"

mv $LOG_FILE $TEMP_LOG

{

cd /home/admn/Downloads/Test;

echo "Cleanup log:" `date`

find /home/admn/Downloads/Test/Edge_Profile_*.tgz -type f \( -mtime +5 -printf 'Y\t' -o -printf 'N\t' \) -printf '%A@\t%p\0' |
    sort -zk2,2 | head -zn -1 | while read -r -d '' flag _ file; do \
        case "$flag" in 
            'Y') echo rm "$file" 
               ;; 
            *) echo "skipping $file (too new)"
               ;; 
        esac;
done

echo

find /home/admn/Downloads/Test/Firefox_Profile_*.tgz -type f \( -printf 'Y\t' -o -printf 'N\t' \) -printf '%A@\t%p\0' |
    sort -zk2,2 | head -zn -1 | while read -r -d '' flag _ file; do \
        case "$flag" in 
            'Y') echo rm "$file" 
               ;; 
            *) echo "skipping $file (too new)"
               ;; 
        esac        
done

} &>> $LOG_FILE

cat $TEMP_LOG >>$LOG_FILE

exit;

在日志文件中输出echo

/usr/local/scripts/cleanup.sh

rm /home/admn/Downloads/Test/Edge_Profile_Jul_06_2021_00-35.tgz
rm /home/admn/Downloads/Test/Edge_Profile_Jul_07_2021_00-35.tgz
....
skipping /home/admn/Downloads/Test/Edge_Profile_Jul_12_2021_00-35.tgz (too new)
skipping /home/admn/Downloads/Test/Edge_Profile_Jul_13_2021_00-35.tgz (too new)
....

rm /home/admn/Downloads/Test/Firefox_Profile_Jul_01_2021_00-35.tgz
rm /home/admn/Downloads/Test/Firefox_Profile_Jul_02_2021_00-35.tgz
....

在实际运行脚本时在日志文件中输出,无需echo

/home/admn/Downloads/cleanup.sh: line 24: skipping /home/admn/Downloads/Test/Edge_Profile_Jul_12_2021_00-35.tgz (too new): No such file or directory
/home/admn/Downloads/cleanup.sh: line 24: skipping /home/admn/Downloads/Test/Edge_Profile_Jul_13_2021_00-35.tgz (too new): No such file or directory
....

目录中的文件总数:20 个文件

1、Edge_Profile_*.tgz:7月6日至7月17日:12个文件

2、Firefox_Profile_*.tgz:7月1日至7月8日:8个文件

问题

(1)我思考该脚本可以运行,但我不太确定,因为我修改了大部分内容但不知道发生了什么。

(2)输出到日志文件:

我希望日志文件中的输出与所获得的输出完全相同echo,除了文件名(不带完整路径),例如:

rm Edge_Profile_Jul_11_2021_00-35.tgz

skipping Edge_Profile_Jul_12_2021_00-35.tgz (too new)

操作系统:Ubuntu MATE 21.04

多谢。

答案1

在可以直接访问文件的 shell 中,根据文件的修改时间进行操作要容易得多。zsh 就是这样一个 shell。只需sudo apt install zsh安装它即可。由于您的文件似乎位于一个目录中,因此此答案是非递归的。首先演示更简单的情况:

  • 保留 Firefox_Profile_*.tgz 的最新副本并删除其余文件(无论它们有多旧):

    echo would rm -v -- Firefox_Profile_*.tgz(.om[2,-1])
    

    echo would如果看起来正确,则删除该部分。这使用glob(通配符)限定符括号内做三件事:

    • 仅选择普通文件(不是目录或套接字等).
    • 按修改时间对文件进行排序,从最新到最旧,om
    • 从第二个元素开始到末尾选择结果列表的一部分 - 跳过第一个(最新)文件,使用[2,-1]

    如果没有匹配的文件,zsh 将停止并显示“zsh:未找到匹配项”,并且不会执行rm

  • 为了保留 Edge_Profile_*.tgz 的最新副本并删除超过 5 天的其余副本,首先我们获取最新的副本:

    newest=(Edge_Profile_*.tgz(.om[1]))
    

    ...然后我们得到超过五天的数据:

    older=(Edge_Profile_*.tgz(.m+5))
    

    这里的新部分是+5修饰符m。它选择超过 5 天的文件。之后,我们确保最新的文件不在要删除的列表中:

    remove=("${(@)older:|newest}")
    

    这里的新部分是数组减法符号:|;它记录在zsh 手册的参数扩展部分。它选择“older”中不属于“newest”的元素。最后,我们删除该文件列表:

    echo would rm -v -- "${remove[@]}"
    

相关内容