linux_sync_many_file_dir
Rsync 多目录/文件增量同步工具
一键脚本
- 多目录同步
bash <(curl -sL gitee.com/meimolihan/linux-command_sh/raw/master/linux_sync_many_file_dir.sh) /etc/nginx /backup/nginx /mnt/nginx
- 多文件同步
bash <(curl -sL gitee.com/meimolihan/linux-command_sh/raw/master/linux_sync_many_file_dir.sh) -f /tmp/test-01.txt /tmp/test-02.txt /tmp/test-02.txt
效果预览
补充说明
该脚本用于将一个源目录或源文件同步到多个目标位置,支持目录和文件两种同步模式,自动记录日志并配置日志轮转,适合需要将配置或数据分发到多个备份点的场景。
功能特点
- 双模式同步:默认同步目录(
rsync.sh 源目录 目标1 目标2 ...),-f选项同步文件(rsync.sh -f 源文件 目标1 目标2 ...) - 多目标同步:一个源同步到任意多个目标位置
- 自动日志:日志保存到
/var/log/rsync/,自动配置 logrotate 轮转(保留4份、最长15天) - 完成度估算:目录同步完成后计算目标大小占源大小的百分比
- 耗时统计:显示同步总用时(时分秒格式)
- 目录自动创建:目标目录不存在时自动创建
- 自动安装依赖:未安装 rsync 时自动安装
- 彩色输出:信息、成功、警告、错误使用不同颜色区分
传参说明
./linux_sync_many_file_dir.sh [选项] <源路径> <目标1> [目标2 ...]
| 选项 | 说明 |
|---|---|
-f |
启用文件同步模式(默认是目录同步模式) |
示例
# 多目录同步:将 /etc/nginx 同步到两个目标
./linux_sync_many_file_dir.sh /etc/nginx /backup/nginx /mnt/nginx
# 多文件同步:将 test.txt 同步到两个目标
./linux_sync_many_file_dir.sh -f test.txt /backup/test.txt /mnt/test.txt
# 三个目标目录同步
./linux_sync_many_file_dir.sh /var/www /backup/www /mnt/www /remote/www
注意事项
- 目录同步模式使用
--delete参数,目标中源不存在的文件会被删除 - 目录同步模式使用
--no-links跳过符号链接 - 日志文件自动轮转,保留最近4份,最长15天
- 需要 sudo 权限写入
/var/log/rsync/日志目录 - 目标路径的父目录不存在时自动创建
脚本源码
#!/bin/bash
set -euo pipefail
gl_hui='\e[37m'
gl_hong='\033[31m'
gl_lv='\033[32m'
gl_huang='\033[33m'
gl_lan='\033[34m'
gl_zi='\033[35m'
gl_bufan='\033[96m'
gl_bai='\033[0m'
log_info() { echo -e "${gl_lan}[信息]${gl_bai} $*"; }
log_ok() { echo -e "${gl_lv}[成功]${gl_bai} $*"; }
log_warn() { echo -e "${gl_huang}[警告]${gl_bai} $*"; }
log_error() { echo -e "${gl_hong}[错误]${gl_bai} $*" >&2; }
DEFAULT_LOG="/var/log/rsync/linux_sync_many_file_dir.log"
LOG_FILE="$DEFAULT_LOG"
LOG_DIR=$(dirname "$LOG_FILE")
mkdir -p "$LOG_DIR"
ROTATE_CONF="/etc/logrotate.d/rsync"
if [[ ! -f "$ROTATE_CONF" ]]; then
sudo tee "$ROTATE_CONF" >/dev/null <<'EOF'
/var/log/rsync/*.log {
daily rotate 4 maxage 15 compress delaycompress
missingok notifempty copytruncate
}
EOF
fi
log(){
local color=$1; shift
echo -e "${color}${*}${gl_bai}"
echo "[$(date '+%F %T')] $*" >> "$LOG_FILE"
}
echo -e "${gl_bai}
╔══════════════════════════════════════════════════════════════════════╗
║ 多目录/文件增量同步工具 linux_sync_many_file_dir.sh ${gl_huang}★ v1.3 ★${gl_bai} ║
║ 一键多目标 · 彩色日志 · 完成度估算 · 耗时统计 ║
║ ${gl_huang}使用说明:${gl_bai} ║
║ - 多目录同步:${gl_zi}linux_sync_many_file_dir.sh ${gl_hong}/etc/nginx ${gl_huang}/backup/nginx ${gl_lv}/mnt/nginx${gl_bai} ║
║ - 多文件同步:${gl_zi}linux_sync_many_file_dir.sh -f ${gl_hong}test.txt ${gl_huang}/backup/test.txt ${gl_lv}/mnt/test.txt${gl_bai} ║
╚══════════════════════════════════════════════════════════════════════╝
${gl_bai}"
log_info "当前生效日志文件:${gl_huang}${LOG_FILE}${gl_bai}"
log_info "日志存放目录:${gl_huang}${LOG_DIR}${gl_bai}"
SINGLE_FILE=false
if [[ ${1:-} == "-f" ]]; then
SINGLE_FILE=true
shift
fi
if [[ $# -lt 2 ]]; then
log_error "用法1(多目录同步): ${gl_zi}$0${gl_bai} <${gl_hong}源目录${gl_bai}> <${gl_huang}目标目录1${gl_bai}> [${gl_lv}目标目录2${gl_bai}] "
log_error "用法2(多文件同步): ${gl_zi}$0 -f${gl_bai} <${gl_hong}源文件${gl_bai}> <${gl_huang}目标文件1${gl_bai}> [${gl_lv}目标文件2${gl_bai}] "
exit 1
fi
source_path="${1%/}"; shift
targets=("$@")
{ echo ""; echo "========== linux_sync_many_file_dir.sh 启动:$(date '+%F %T') =========="; } >> "$LOG_FILE"
if ! $SINGLE_FILE; then
log_info "${gl_bai}检查源目录是否存在${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
[[ -d $source_path ]] || { log_error "${gl_bai}源目录不存在:${gl_hong}$source_path${gl_bai}"; exit 1; }
log_ok "${gl_bai}源目录存在:${gl_huang}$source_path${gl_bai}"
else
log_info "${gl_bai}检查源文件是否存在${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
[[ -f $source_path ]] || { log_error "${gl_bai}源文件不存在:${gl_hong}$source_path${gl_bai}"; exit 1; }
log_ok "${gl_bai}源文件存在:${gl_huang}$source_path${gl_bai}"
fi
log_info "${gl_bai}检查 ${gl_huang}rsync${gl_bai} 是否安装${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
if ! command -v rsync &>/dev/null; then
log_warn "${gl_huang}rsync ${gl_bai}未安装,正在尝试自动安装${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
sudo apt-get update -qq && sudo apt-get install -y rsync || {
log_error "${gl_huang}rsync${gl_bai} 安装失败,请手动安装后重试"; exit 1
}
fi
log_ok "${gl_huang}rsync${gl_bai} 已就绪"
sync_and_check(){
local tgt=$1; local idx=$2
local tgt_dir=$(dirname "$tgt")
if [[ ! -d $tgt_dir ]]; then
log_warn "${gl_bai}目标目录不存在,正在创建:${gl_huang}$tgt_dir${gl_bai}"
mkdir -p "$tgt_dir" || { log_error "创建失败:$tgt_dir"; return 1; }
fi
if $SINGLE_FILE; then
log_warn "${gl_bai}开始同步文件:${gl_huang}$source_path${gl_bai} → ${gl_lv}$tgt${gl_bai}"
rsync -avh --progress "$source_path" "$tgt" 2>&1 | tee -a "$LOG_FILE"
else
log_warn "${gl_bai}开始同步目录:${gl_huang}$source_path/${gl_bai} → ${gl_lv}$tgt/${gl_bai}"
rsync -avhzp --progress --no-links --delete "$source_path/" "$tgt/" 2>&1 | tee -a "$LOG_FILE"
fi
local st=${PIPESTATUS[0]}
if [[ $st -eq 0 ]]; then
if $SINGLE_FILE; then
log_ok "${gl_bai}文件同步成功(目标${gl_huang}$idx${gl_bai})"
else
local src_size=$(du -sb "$source_path" | awk '{print $1}')
local tgt_size=$(du -sb "$tgt" | awk '{print $1}')
local comp=$(awk -v s=$src_size -v t=$tgt_size 'BEGIN{printf "%.2f",(t/s)*100}')
log_ok "${gl_bai}目录同步成功(目标${gl_huang}$idx${gl_bai}) 完成度约 ${gl_lv}${comp}${gl_bai}%"
fi
else
log_error "同步失败(目标${gl_huang}$idx${gl_bai}),请查看日志"
fi
}
start_time=$(date '+%F %T'); start_ts=$(date +%s)
log_info "${gl_bai}同步开始时间:${gl_lv}$start_time${gl_bai}"
for i in "${!targets[@]}"; do
echo -e "${gl_bufan}----------------------------------------${gl_bai}"
sync_and_check "${targets[$i]}" "$((i+1))"
done
end_time=$(date '+%F %T'); end_ts=$(date +%s)
total=$((end_ts - start_ts))
printf -v dur "%d时%02d分%02d秒" $((total/3600)) $(((total%3600)/60)) $((total%60))
echo -e "${gl_bufan}----------------------------------------${gl_bai}"
log_info "${gl_bai}同步结束时间:${gl_hong}$end_time${gl_bai}"
log_info "总用时:${gl_lv}$dur${gl_bai}"
if $SINGLE_FILE; then
echo -e "${gl_lan}[信息]${gl_bai} 源文件:${gl_huang}$source_path${gl_bai}"
echo -e "${gl_lan}[信息]${gl_bai} 目标文件列表:"
for target in "${targets[@]}"; do
echo -e "${gl_lv} ${target}${gl_bai}"
done
{
echo "[源文件] $source_path"
echo "[目标文件列表]"
printf ' %s\n' "${targets[@]}"
} >> "$LOG_FILE"
else
echo -e "${gl_lan}[信息]${gl_bai} 源目录:${gl_huang}$source_path${gl_bai}"
echo -e "${gl_lan}[信息]${gl_bai} 目标目录列表:"
for target in "${targets[@]}"; do
echo -e "${gl_lv} ${target}${gl_bai}"
done
{
echo "[源目录] $source_path"
echo "[目标目录列表]"
printf ' %s\n' "${targets[@]}"
} >> "$LOG_FILE"
fi
echo -e "${gl_bufan}----------------------------------------${gl_bai}"
log_ok "完整日志已保存至:${gl_huang}$LOG_FILE${gl_bai}"
echo -e "${gl_bufan}----------------------------------------${gl_bai}"
{ echo "========== linux_sync_many_file_dir.sh 结束:$end_time =========="; echo ""; } >> "$LOG_FILE"
创建本地脚本
new_script="linux_sync_many_file_dir.sh"
cat > "$new_script" << 'EOF'
#!/bin/bash
# 粘贴脚本源码
EOF
chmod +x "$new_script" && ./"$new_script" && rm -f "$new_script"