From 0d103b931d32eded52b98f9c459424cef591a797 Mon Sep 17 00:00:00 2001 From: HA Date: Mon, 27 Apr 2026 00:00:42 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backup/backup.sh | 62 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/backup/backup.sh b/backup/backup.sh index 23a68bc..4a82632 100644 --- a/backup/backup.sh +++ b/backup/backup.sh @@ -562,12 +562,16 @@ smb_retention_smbclient() { else cutoff=$(date -d "-${COMMON_RETENTION_DAYS} days" +%s) fi - log "清理远端早于 ${COMMON_RETENTION_DAYS} 天的备份目录" - dbg "cutoff=$cutoff ($(date -r "$cutoff" 2>/dev/null || date -d "@$cutoff" 2>/dev/null))" + log "清理远端早于 ${COMMON_RETENTION_DAYS} 天的备份目录(cutoff=$(date -r "$cutoff" 2>/dev/null || date -d "@$cutoff" 2>/dev/null))" + dbg "cutoff=$cutoff" + # 先把符合命名规范的目录列出来;用 process substitution 而不是管道,避免 while 跑在子 shell 中导致计数器丢失 # smbclient ls 输出形如: name FLAGS size weekday mon dd hh:mm:ss yyyy # 目录会带 D 标志;这里直接以名字模式 + D 标志双重匹配,减少误删风险。 - echo "$listing" | awk '$2 ~ /D/ {print $1}' | grep -E "^${COMMON_ARCHIVE_PREFIX}-[0-9]{8}-[0-9]{6}\$" | while read -r dir; do + local scanned=0 deleted=0 kept=0 failed=0 dir + while read -r dir; do + [[ -z "$dir" ]] && continue + scanned=$((scanned+1)) local d="${dir#${COMMON_ARCHIVE_PREFIX}-}" d="${d%%-*}" # YYYYmmdd local t="${dir##*-}" # HHMMSS @@ -578,14 +582,28 @@ smb_retention_smbclient() { else ftime=$(date -d "$iso" +%s) fi - dbg " 远端目录 $dir -> ftime=$ftime" + dbg " 远端目录 $dir -> ftime=$ftime ($iso)" if [[ "$ftime" -lt "$cutoff" ]]; then - log "删除远端旧备份目录:$dir" + log " 删除:$dir(备份时间 $iso)" # 先清空目录内文件,再 rmdir smbclient "${args[@]}" -D "${SMB_PATH:-/}/${dir}" -c "prompt OFF; mask *; del *" >/dev/null 2>&1 - smbclient "${args[@]}" -D "${SMB_PATH:-/}" -c "rmdir \"${dir}\"" >/dev/null 2>&1 || warn "rmdir 失败:$dir" + if smbclient "${args[@]}" -D "${SMB_PATH:-/}" -c "rmdir \"${dir}\"" >/dev/null 2>&1; then + deleted=$((deleted+1)) + else + warn " rmdir 失败:$dir" + failed=$((failed+1)) + fi + else + dbg " 保留:$dir(备份时间 $iso,未到保留期)" + kept=$((kept+1)) fi - done + done < <(echo "$listing" | awk '$2 ~ /D/ {print $1}' | grep -E "^${COMMON_ARCHIVE_PREFIX}-[0-9]{8}-[0-9]{6}\$") + + if [[ $scanned -eq 0 ]]; then + log "远端无符合命名规范的备份目录,无需清理" + else + log "清理完成:扫描 ${scanned} 个目录,删除 ${deleted},保留 ${kept}$([[ $failed -gt 0 ]] && echo ",失败 ${failed}")" + fi } run_smb() { @@ -705,11 +723,15 @@ rclone_retention() { else cutoff=$(date -d "-${COMMON_RETENTION_DAYS} days" +%s) fi - log "清理远端早于 ${COMMON_RETENTION_DAYS} 天的备份目录" + log "清理远端早于 ${COMMON_RETENTION_DAYS} 天的备份目录(cutoff=$(date -r "$cutoff" 2>/dev/null || date -d "@$cutoff" 2>/dev/null))" dbg "cutoff=$cutoff parent=$parent" # rclone lsf --dirs-only 输出形如 "name/",去掉尾随斜杠后做名字匹配 - echo "$listing" | sed 's:/$::' | grep -E "^${COMMON_ARCHIVE_PREFIX}-[0-9]{8}-[0-9]{6}\$" | while read -r dir; do + # 用 process substitution 而非管道,保证 while 在当前 shell 中跑,计数器不丢 + local scanned=0 deleted=0 kept=0 failed=0 dir + while read -r dir; do + [[ -z "$dir" ]] && continue + scanned=$((scanned+1)) local d="${dir#${COMMON_ARCHIVE_PREFIX}-}" d="${d%%-*}" # YYYYmmdd local t="${dir##*-}" # HHMMSS @@ -720,14 +742,26 @@ rclone_retention() { else ftime=$(date -d "$iso" +%s) fi - dbg " 远端目录 $dir -> ftime=$ftime" + dbg " 远端目录 $dir -> ftime=$ftime ($iso)" if [[ "$ftime" -lt "$cutoff" ]]; then - log "删除远端旧备份目录:$dir" - if ! rclone_cmd purge "${parent}/${dir}" >/dev/null 2>&1; then - warn "purge 失败:$dir" + log " 删除:${parent}/${dir}(备份时间 $iso)" + if rclone_cmd purge "${parent}/${dir}" >/dev/null 2>&1; then + deleted=$((deleted+1)) + else + warn " purge 失败:$dir" + failed=$((failed+1)) fi + else + dbg " 保留:$dir(备份时间 $iso,未到保留期)" + kept=$((kept+1)) fi - done + done < <(echo "$listing" | sed 's:/$::' | grep -E "^${COMMON_ARCHIVE_PREFIX}-[0-9]{8}-[0-9]{6}\$") + + if [[ $scanned -eq 0 ]]; then + log "远端无符合命名规范的备份目录,无需清理" + else + log "清理完成:扫描 ${scanned} 个目录,删除 ${deleted},保留 ${kept}$([[ $failed -gt 0 ]] && echo ",失败 ${failed}")" + fi } run_rclone() {