Files
docker-backup/bin/cleanup.sh
Wang Defa 1ea8d681ee fix: 修复日志输出干扰函数返回值的问题
问题描述:
- backup_folders() 和 backup_mysql() 函数使用 echo 返回文件路径
- 但 log_info 也输出到 stdout,导致主函数捕获了所有日志而非路径
- 最终 merge_backups 收到空参数,显示"没有需要打包的文件"

修复内容:
- 将所有日志输出重定向到 stderr (>&2)
- 简化 tar 命令的输出处理逻辑
- 确保函数返回值只包含文件路径

影响文件:
- bin/backup.sh - log() 函数添加 >&2 重定向
- bin/cleanup.sh - 所有 log 函数添加 >&2 重定向
2025-12-25 15:40:36 +08:00

250 lines
7.6 KiB
Bash
Executable File

#!/bin/bash
###############################################################################
# Docker Backup Cleanup Script
# 功能:清理旧的备份文件
###############################################################################
set -e
set -o pipefail
# 脚本所在目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# 配置文件路径(系统级配置)
CONFIG_FILE="${CONFIG_FILE:-/etc/docker-backup/config.yml}"
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
###############################################################################
# 日志函数
###############################################################################
log_info() {
echo -e "${GREEN}[INFO]${NC} $@" >&2
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $@" >&2
}
log_error() {
echo -e "${RED}[ERROR]${NC} $@" >&2
}
###############################################################################
# 检查依赖
###############################################################################
check_dependencies() {
if ! command -v yq &> /dev/null; then
log_error "缺少依赖工具: yq"
log_error "请运行 install.sh 安装依赖"
exit 1
fi
}
###############################################################################
# 加载配置
###############################################################################
load_config() {
log_info "加载配置文件: ${CONFIG_FILE}"
if [[ ! -f "${CONFIG_FILE}" ]]; then
log_error "配置文件不存在: ${CONFIG_FILE}"
exit 1
fi
OUTPUT_DIR=$(yq eval '.backup.output_dir' "${CONFIG_FILE}")
BACKUP_PREFIX=$(yq eval '.backup.prefix' "${CONFIG_FILE}")
RETENTION_ENABLED=$(yq eval '.backup.retention.enabled' "${CONFIG_FILE}")
KEEP_DAYS=$(yq eval '.backup.retention.keep_days' "${CONFIG_FILE}")
KEEP_COUNT=$(yq eval '.backup.retention.keep_count' "${CONFIG_FILE}")
}
###############################################################################
# 清理旧备份(按天数)
###############################################################################
cleanup_by_days() {
local keep_days=$1
log_info "删除 ${keep_days} 天前的备份文件..."
local deleted_count=0
while IFS= read -r old_file; do
log_info "删除: ${old_file}"
rm -f "${old_file}"
((deleted_count++))
done < <(find "${OUTPUT_DIR}" -name "${BACKUP_PREFIX}-*.tar.gz" -type f -mtime +${keep_days})
log_info "共删除 ${deleted_count} 个旧备份文件"
}
###############################################################################
# 清理旧备份(按数量)
###############################################################################
cleanup_by_count() {
local keep_count=$1
log_info "保留最近 ${keep_count} 个备份文件..."
# 获取所有备份文件,按时间排序
local all_backups=($(find "${OUTPUT_DIR}" -name "${BACKUP_PREFIX}-*.tar.gz" -type f -printf '%T@ %p\n' | sort -rn | awk '{print $2}'))
local total_count=${#all_backups[@]}
local deleted_count=0
if [[ ${total_count} -le ${keep_count} ]]; then
log_info "当前备份数量 ${total_count} 不超过保留数量 ${keep_count},无需清理"
return 0
fi
# 删除超出数量的备份
for ((i=${keep_count}; i<${total_count}; i++)); do
local old_file="${all_backups[$i]}"
log_info "删除: ${old_file}"
rm -f "${old_file}"
((deleted_count++))
done
log_info "共删除 ${deleted_count} 个旧备份文件"
}
###############################################################################
# 列出所有备份
###############################################################################
list_backups() {
log_info "=========================================="
log_info "备份文件列表"
log_info "=========================================="
if [[ ! -d "${OUTPUT_DIR}" ]]; then
log_warn "备份目录不存在: ${OUTPUT_DIR}"
return 0
fi
local backup_files=($(find "${OUTPUT_DIR}" -name "${BACKUP_PREFIX}-*.tar.gz" -type f -printf '%T@ %p %s\n' | sort -rn))
if [[ ${#backup_files[@]} -eq 0 ]]; then
log_info "没有找到备份文件"
return 0
fi
local count=0
while IFS=' ' read -r timestamp filepath size; do
local file_date=$(date -d "@${timestamp}" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || date -r "${timestamp}" '+%Y-%m-%d %H:%M:%S')
local file_size=$(numfmt --to=iec-i --suffix=B "${size}" 2>/dev/null || echo "${size} bytes")
((count++))
printf "%3d. %s %s %s\n" "${count}" "${file_date}" "${file_size}" "$(basename "${filepath}")"
done < <(find "${OUTPUT_DIR}" -name "${BACKUP_PREFIX}-*.tar.gz" -type f -printf '%T@ %p %s\n' | sort -rn)
log_info "=========================================="
log_info "总计: ${count} 个备份文件"
}
###############################################################################
# 显示使用帮助
###############################################################################
show_usage() {
cat << EOF
Docker Backup 清理工具
用法: $0 [选项]
选项:
-h, --help 显示此帮助信息
-l, --list 列出所有备份文件
-d, --days DAYS 删除 N 天前的备份文件
-c, --count COUNT 只保留最近 N 个备份文件
-a, --auto 使用配置文件中的清理策略自动清理
示例:
$0 --list # 列出所有备份
$0 --days 7 # 删除 7 天前的备份
$0 --count 10 # 只保留最近 10 个备份
$0 --auto # 使用配置文件的策略清理
EOF
}
###############################################################################
# 主函数
###############################################################################
main() {
# 检查依赖
check_dependencies
# 加载配置
load_config
# 解析参数
if [[ $# -eq 0 ]]; then
show_usage
exit 0
fi
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
show_usage
exit 0
;;
-l|--list)
list_backups
exit 0
;;
-d|--days)
if [[ -z "$2" ]] || [[ ! "$2" =~ ^[0-9]+$ ]]; then
log_error "无效的天数参数: $2"
exit 1
fi
cleanup_by_days "$2"
exit 0
;;
-c|--count)
if [[ -z "$2" ]] || [[ ! "$2" =~ ^[0-9]+$ ]]; then
log_error "无效的数量参数: $2"
exit 1
fi
cleanup_by_count "$2"
exit 0
;;
-a|--auto)
if [[ "${RETENTION_ENABLED}" != "true" ]]; then
log_warn "配置文件中未启用自动清理"
exit 0
fi
if [[ "${KEEP_DAYS}" != "null" ]] && [[ "${KEEP_DAYS}" =~ ^[0-9]+$ ]]; then
cleanup_by_days "${KEEP_DAYS}"
elif [[ "${KEEP_COUNT}" != "null" ]] && [[ "${KEEP_COUNT}" =~ ^[0-9]+$ ]]; then
cleanup_by_count "${KEEP_COUNT}"
else
log_error "配置文件中未设置有效的清理策略"
exit 1
fi
exit 0
;;
*)
log_error "未知选项: $1"
show_usage
exit 1
;;
esac
done
}
# 执行主函数
main "$@"