#!/bin/bash
# --- 配置区 ---
# 定义来源目录和对应的远程目标完整路径。
# 这两个数组的元素数量必须相同,且索引一一对应。
#
# 重要提示:
# - 如果 SOURCE_PATHS 中的条目以斜杠结尾(例如 "/path/to/local/dir1/"),
# rsync 会将该目录的 *内容* 同步到 DEST_REMOTE_FULL_PATHS 中对应的路径。
# 示例:SOURCE="/local/data/", DEST="/remote/backup/data/" -> /local/data/ 的内容会进入 /remote/backup/data/
# - 如果 SOURCE_PATHS 中的条目不以斜杠结尾(例如 "/path/to/local/dir1"),
# rsync 会将该目录 *本身* 同步到 DEST_REMOTE_FULL_PATHS 中对应的路径。
# 示例:SOURCE="/local/data", DEST="/remote/backup/" -> /local/data 会被同步为 /remote/backup/data
#
# 请根据您的实际需求配置以下路径:
SOURCE_PATHS=(
"/path/to/local/dir1/" # 来源目录1:请替换为您的实际路径
"/path/to/local/dir2" # 来源目录2:请替换为您的实际路径
"/path/to/local/another_dir/" # 来源目录3:请替换为您的实际路径
# 您可以继续添加更多来源目录
)
DEST_REMOTE_FULL_PATHS=(
"/remote/backup/dir1_destination/" # 来源目录1对应的远程目标完整路径
"/remote/backup/dir2_destination/" # 来源目录2对应的远程目标完整路径
"/remote/another_backup_location/" # 来源目录3对应的远程目标完整路径
# 请确保这里有与 SOURCE_PATHS 相同数量的对应路径
)
DEST_USER="your_username" # 目标服务器用户名,请替换
DEST_HOST="your_server_ip" # 目标服务器IP,请替换
SSH_PORT="22" # SSH 端口,请替换
LOG_BASE_DIR="/var/log/rsync_sync" # 日志文件存放的基础目录,请替换为您的实际路径
LOG_FILE_PREFIX="sync_multi_dir_log" # 日志文件前缀
LOG_RETENTION_DAYS=7 # 日志保留天数
# --- rsync 选项 ---
# 使用数组来存储 rsync 选项,避免 eval 的复杂性
# -a: 归档模式,等同于 -rlptgoD (递归、保留符号链接、权限、时间戳、组、所有者、设备文件)
# -v: 详细输出
# -z: 压缩传输数据
# -u: 跳过目标端更新的文件 (如果目标文件比源文件新,则不覆盖)
# --delete: 删除源目录中不存在的目标目录文件 (重要:这会删除远程目标目录中源目录没有的文件)
# -e "ssh -p $SSH_PORT": 指定SSH命令和端口
# --exclude "*.mp4": 排除所有以 .mp4 结尾的文件
# --exclude "*.strm": 排除所有以 .strm 结尾的文件
RSYNC_OPTIONS_ARRAY=(
-avzu
--delete
-e "ssh -p $SSH_PORT"
--exclude "*.mp4"
--exclude "*.strm"
)
# --- 函数定义 ---
# 确保日志目录存在
ensure_log_dir() {
if [ ! -d "$LOG_BASE_DIR" ]; then
mkdir -p "$LOG_BASE_DIR"
if [ $? -ne 0 ]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") ERROR: Failed to create log directory $LOG_BASE_DIR. Exiting."
exit 1
fi
fi
}
# 记录日志函数
log_message() {
local type="$1" # INFO, WARNING, ERROR
local message="$2"
echo "$(date +"%Y-%m-%d %H:%M:%S") [$type] $message" | tee -a "$CURRENT_LOG_FILE"
}
# 清理旧日志函数
clean_old_logs() {
log_message "INFO" "Cleaning old logs older than $LOG_RETENTION_DAYS days..."
find "$LOG_BASE_DIR" -name "${LOG_FILE_PREFIX}_*.log" -type f -mtime +$LOG_RETENTION_DAYS -delete
if [ $? -eq 0 ]; then
log_message "INFO" "Old logs cleaned successfully."
else
log_message "WARNING" "Failed to clean old logs. Please check permissions."
fi
}
# --- 主脚本逻辑 ---
# 获取当前日期和时间,并格式化为YYYY-MM-DD-HH-MM-S
CURRENT_DATETIME=$(date +"%Y-%m-%d-%H-%M-%S")
CURRENT_LOG_FILE="$LOG_BASE_DIR/${LOG_FILE_PREFIX}_$CURRENT_DATETIME.log"
echo "--- rsync 多目录同步脚本开始 ---"
echo "日志文件: $CURRENT_LOG_FILE"
ensure_log_dir
log_message "INFO" "Starting rsync synchronization for multiple directories."
log_message "INFO" "rsync options: ${RSYNC_OPTIONS_ARRAY[*]}"
# 检查来源路径和目标路径数组的元素数量是否匹配
if [ ${#SOURCE_PATHS[@]} -ne ${#DEST_REMOTE_FULL_PATHS[@]} ]; then
log_message "ERROR" "配置错误:SOURCE_PATHS 数组的元素数量 (${#SOURCE_PATHS[@]}) 与 DEST_REMOTE_FULL_PATHS 数组的元素数量 (${#DEST_REMOTE_FULL_PATHS[@]}) 不匹配。请检查配置。"
exit 1
fi
TOTAL_DIRS=${#SOURCE_PATHS[@]}
SYNC_SUCCESS_COUNT=0
SYNC_FAIL_COUNT=0
# 遍历 SOURCE_PATHS 数组,对每个目录执行同步
for i in "${!SOURCE_PATHS[@]}"; do
SOURCE_DIR="${SOURCE_PATHS[$i]}"
REMOTE_DEST_PATH="${DEST_REMOTE_FULL_PATHS[$i]}" # 获取对应的远程目标完整路径
log_message "INFO" "--- 正在处理第 $((i+1)) 个目录,共 $TOTAL_DIRS 个:$SOURCE_DIR ---"
# 构建完整的远程目标字符串 (user@host:path)
REMOTE_TARGET="$DEST_USER@$DEST_HOST:$REMOTE_DEST_PATH"
log_message "INFO" "正在同步 '$SOURCE_DIR' 到 '$REMOTE_TARGET'"
# 执行rsync命令并将输出追加到日志文件
rsync "${RSYNC_OPTIONS_ARRAY[@]}" "$SOURCE_DIR" "$REMOTE_TARGET" 2>&1 | tee -a "$CURRENT_LOG_FILE"
RSYNC_EXIT_CODE=${PIPESTATUS[0]} # 获取rsync的退出状态码
if [ $RSYNC_EXIT_CODE -eq 0 ]; then
log_message "INFO" "目录 '$SOURCE_DIR' 同步成功。"
SYNC_SUCCESS_COUNT=$((SYNC_SUCCESS_COUNT + 1))
elif [ $RSYNC_EXIT_CODE -eq 24 ]; then
log_message "WARNING" "目录 '$SOURCE_DIR' 同步完成,但有部分文件未传输,可能是因为源文件在同步过程中消失。(退出码 24)"
log_message "WARNING" "如果文件在同步期间被修改,这可能是正常的,但请检查是否出现意外情况。"
SYNC_SUCCESS_COUNT=$((SYNC_SUCCESS_COUNT + 1)) # 视为成功,但有警告
else
log_message "ERROR" "目录 '$SOURCE_DIR' 同步失败,退出码:$RSYNC_EXIT_CODE"
# 可以在这里添加邮件通知或其他告警机制,针对单个目录的失败
SYNC_FAIL_COUNT=$((SYNC_FAIL_COUNT + 1))
fi
log_message "INFO" "---------------------------------------------------"
done
log_message "INFO" "所有目录同步完成。"
log_message "INFO" "总结:$SYNC_SUCCESS_COUNT 个目录同步成功 (包括警告),$SYNC_FAIL_COUNT 个目录同步失败。"
# 清理旧日志
clean_old_logs
log_message "INFO" "脚本执行完毕。"
echo "--- rsync 多目录同步脚本结束 ---"