雨夜

博客即是笔记,常记常看

导航

【Linux】备份

Posted on 2025-10-18 16:14  二月无雨  阅读(8)  评论(0)    收藏  举报
备份日志

#!/bin/bash
set -euo pipefail
set -u  # 启用未定义变量检查
# ================================== 配置参数 ==================================
TARGET_DIR="/data/webapps_djypt_gdyc/logs"                   # 执行目录(脚本所在目录或当前工作目录)
BACKUP_ROOT="/backup/dated_logs"                             # 备份根目录
RETENTION_DAYS=7                                             # 备份保留天数
BACKUP_LOG="/var/log/backup_dated_logs.log"                  # 脚本运行日志
DATE_PATTERN="2[0-9]{3}-[0-1][0-9]-[0-3][0-9]"               # 日志文件名中的日期格式(YYYY-MM-DD)
# ==============================================================================

# 初始化变量
BACKUP_TODAY_DIR="${BACKUP_ROOT}/"
DUPLICATE_MARKER="_duplicate_$(date +"%H%M%S")"              # 重复文件标记(加时间戳避免冲突)

# 日志函数
log() {
    echo "[$(date +"%Y-%m-%d %H:%M:%S")] $1" >> "${BACKUP_LOG}"
}

# 初始化
log "===== 开始每日带日期日志备份 ====="
mkdir -p "${BACKUP_TODAY_DIR}" || { log "错误:无法创建备份目录 ${BACKUP_TODAY_DIR}"; exit 1; }

# 1. 查找目标目录下所有含日期格式的日志文件
# 格式要求:文件名包含 YYYYMMDD 日期(如 app_20251018.log、20251018_error.log)
find_target_logs() {
    find "${TARGET_DIR}" -type f -name "*.gz" -mtime -3 |grep -E "${DATE_PATTERN}"
	find "${TARGET_DIR}" -type f -name "*.gz" |grep -E "${DATE_PATTERN}"
}

# 2. 提取文件的上两级目录结构(用于备份时保留)
# 例:源路径 a/b/c/d/log_2025-10-18.log → 上两级目录为 c/d → 备份路径中保留 c/d
get_upper_two_dirs() {
    local full_path="$1"
    local rel_path="${full_path#${TARGET_DIR}/}"  # 相对于目标目录的路径
    local dir_depth=$(echo "${rel_path}" | tr '/' '\n' | wc -l)  # 目录深度(含文件名)
    
    if [ "${dir_depth}" -le 2 ]; then
        # 若深度≤2(如直接在目标目录或下一级目录),保留完整相对路径
        echo "$(dirname "${rel_path}")"
    else
        # 取上两级目录(通过逆向切割实现)
        echo "${rel_path}" | rev | cut -d'/' -f3- | rev | cut -d'/' -f1-
    fi
}

# 3. 检查备份目录中是否存在同名文件(验证名称重复)
is_name_duplicate() {
    local target_name="$1"
    local backup_subdir="$2"
    [ -f "${BACKUP_TODAY_DIR}/${backup_subdir}/${target_name}" ]
}

# 4. 备份文件并处理重复
backup_log_file() {
    local src_file="$1"
    local file_name=$(basename "${src_file}")
    local upper_two_dirs=$(get_upper_two_dirs "${src_file}")
    local backup_subdir="${upper_two_dirs}"
    local dest_dir="${BACKUP_TODAY_DIR}/${backup_subdir}"
    local dest_file="${dest_dir}/${file_name}"
    
    # 创建备份子目录(保留上两级结构)
    mkdir -p "${dest_dir}" || { log "警告:无法创建备份子目录 ${dest_dir},跳过文件 ${src_file}"; return 1; }
    
    # 检查名称重复
    if is_name_duplicate "${file_name}" "${backup_subdir}"; then
        local new_file_name="${file_name%.log}_${DUPLICATE_MARKER}.log"  # 处理.log后缀
        new_file_name="${new_file_name%.gz}_${DUPLICATE_MARKER}.gz"      # 处理.gz后缀(如需)
        dest_file="${dest_dir}/${new_file_name}"
        log "发现重复文件名 ${file_name},重命名为 ${new_file_name}"
    fi
    
    # 复制文件(保留权限和属性)
    cp -a "${src_file}" "${dest_file}" || { log "错误:备份 ${src_file} 失败"; return 1; }
    
    # 验证备份完整性(对比文件大小)
    if [ $(stat -c%s "${src_file}") -ne $(stat -c%s "${dest_file}") ]; then
        log "错误:备份文件 ${dest_file} 损坏(大小不匹配),已删除"
        rm -f "${dest_file}"
        return 1
    fi
    
    log "备份成功:${src_file} → ${dest_file}"
    return 0
}

# 5. 清理过期备份
cleanup_expired() {
    log "清理 ${RETENTION_DAYS} 天前的备份..."
    find "${BACKUP_ROOT}" -type d -name "20[0-9]{6}" -mtime +${RETENTION_DAYS} -exec rm -rf {} + 2>/dev/null
}

# 主流程
main() {
    local target_logs=$(find_target_logs)
    
    if [ -z "${target_logs}" ]; then
        log "警告:未找到日志文件"
        cleanup_expired
        log "===== 备份结束 ====="
        exit 0
    fi
    
    # 批量备份日志文件
    echo "${target_logs}"|while IFS= read -r log_file; do
        backup_log_file "${log_file}"
    done
    
    # 清理过期备份
    cleanup_expired
    
    log "===== 备份结束 ====="
}

# 启动主流程
main