中文 English

别再盲目执行 rsync!我用 Dry Run 揪出差异文件,再安全完成增量同步

发布时间: 2026-07-11
rsync Linux macOS Windows 11 WSL 增量同步 文件备份 自动化 Agent

先说结论

rsync 最值得养成的习惯,不是背更多参数,而是把同步拆成两步:先用 --dry-run 生成差异计划,确认无误后再执行正式同步。如果还会删除目标端文件,则必须把删除能力单独授权,不能让 --delete 悄悄成为默认动作。

文首那段 rsync -n -avz --delete --out-format="%n" 确实能看到一部分将变化的路径,但它只输出文件名,后续再靠 grep 猜“哪些是文件、哪些是删除”并不稳。更可靠的做法是使用 --itemize-changes,并把 %i%n 一起输出,让每一行自己说明是新增、更新、删除还是目录属性变化。

原创题图:先看差异,再执行 rsync 增量同步

为什么“先比较再同步”比想象中重要

很多同步任务最初都很简单:把本地目录传到服务器、把照片复制到 NAS、把构建产物发布到另一台机器。第一次执行时,目录里只有几十个文件,直接敲一条 rsync -av 看起来没有风险。

但目录会慢慢长大,脚本也会慢慢变成定时任务。几个月后,源目录里可能有几万个文件,目标端还可能保存了一些只存在于目标端的历史资料。这时再随手加上 --delete,含义就不再是“帮我同步”,而是“让目标端严格变成源端的镜像,多出来的东西全部删除”。

这像搬家:正常搬家是把旧房子的箱子搬到新房子;--delete 则相当于搬家公司发现新房子里有旧房子清单之外的东西,就顺手扔掉。它不是错误参数,但必须在你清楚后果时使用。

生活化理解:dry-run 就是搬家前先列清单

从一段常见脚本说起

原始思路大致如下:

#!/bin/bash

DIFF_FILE="/tmp/rsync_diff.txt"

echo "正在比较本地和远程目录..."
rsync -n -avz --delete --out-format="%n" $LOCAL_DIR $REMOTE_DIR > $DIFF_FILE

grep -v '/$' $DIFF_FILE | grep -v 'deleting' > "${DIFF_FILE}_filtered"

它的方向没有错:-n 代表 dry-run,只预演、不修改;输出先写入临时文件,再过滤目录与删除记录。但在真实自动化中,它有几个容易被忽略的问题。

变量没有加引号

$LOCAL_DIR$REMOTE_DIR 如果包含空格、通配符或某些特殊字符,Shell 会重新拆分它们。更稳妥的写法是:

rsync ... "$LOCAL_DIR" "$REMOTE_DIR"

只输出 %n,丢失了变化类型

%n 只告诉你路径名,不告诉你为什么传输。后续脚本只能继续通过字符串过滤猜测。新增文件、内容变化、时间变化和权限变化都可能只剩下一个名字。

grep -v 'deleting' 会隐藏最危险的信息

如果命令启用了 --delete,删除项恰恰应该重点审查。把它们过滤掉,就像审核付款清单时把所有负数金额隐藏了:画面更干净,但风险更大。

源目录末尾的 / 会改变语义

下面两条命令看起来只差一个斜杠:

rsync -av source destination/
rsync -av source/ destination/

前者通常把 source 目录本身放进目标目录;后者同步的是 source 里面的内容。这个区别很小,却经常造成目标端多套一层目录。

本地复制通常不需要 -z

-z 会压缩传输数据,跨较慢网络时可能节省带宽;本机磁盘、局域网高速链路或已经压缩过的图片、视频、压缩包未必受益,反而会消耗 CPU。它不是必须删除的坏参数,而是应该按链路和数据类型决定。

我做了一个真实、隔离的实验

为了避免拿真实文件冒险,我在临时目录中创建了源端和目标端,并故意制造以下差异:

真实截图:同步前源目录与目标目录的文件状态

先运行接近原脚本的命令:

rsync -n -av --delete --out-format="%n" "source/" "destination/"

它确实列出了要删除和要传输的路径,但普通文件行没有明确告诉我们“新增”还是“更新”。当输出要交给下一段脚本、监控系统或 Agent 判断时,这种信息就不够结构化。

更可靠的差异清单:--itemize-changes

我把命令改成:

rsync -ani --delete \
  --out-format="%i|%n%L" \
  "source/" "destination/"

这里的关键不是参数变多,而是输出从“名字列表”升级成了“带动作标签的计划表”:

*deleting photos/old-name.txt
*deleting docs/deleted.txt
>f+++++++|docs/added.txt
>f..t....|docs/modified.txt
>f+++++++|photos/new-name.txt

真实截图:dry-run 输出明确标记新增、更新和删除

常见标记可以先这样理解:

不需要第一天就背下每一位。就像看快递单,先看“寄出、退回、签收”这些大状态,遇到特殊情况再查细节即可。

手机端可读的 rsync itemize 差异标记说明

为什么 rsync 能做到增量同步

rsync 并不是每次都把全部文件重新复制一遍。它先比较文件的基本信息,常见判断包括大小和修改时间;满足条件时才进一步传输。远程同步时,它还能通过 rsync 算法减少需要跨网络发送的数据。

可以把它想成老师检查作业:如果作业本页数和最后修改时间都没变化,老师不会重新抄一份;如果只改了其中一部分,就重点处理变化内容。实际行为还会受到 --checksum、文件系统时间精度、rsync 版本和传输方式影响,因此关键任务不能只凭想象判断,仍应看 dry-run 输出并做同步后验证。

要不要使用 --checksum

--checksum 会读取两端文件内容并计算校验值,能发现“大小和时间碰巧一样、内容却不同”的情况,但代价是两端都要完整读取文件。文件很多、磁盘较慢时,它可能比真正传输还耗时。

日常镜像同步通常先使用默认 quick check;高价值归档、迁移验收或怀疑时间戳不可靠时,再增加 --checksum。安全不是把所有最重参数一次性打开,而是根据风险选择验证强度。

正式同步:删除能力必须单独授权

推荐把默认模式设为“不删除”。只有明确需要镜像语义时,才设置环境变量启用删除:

ALLOW_DELETE=1 ./rsync-safe-sync-ubuntu.sh \
  "/data/source/" \
  "backup-user@<目标地址>:/data/archive/" \
  --plan

确认计划后再执行:

ALLOW_DELETE=1 ./rsync-safe-sync-ubuntu.sh \
  "/data/source/" \
  "backup-user@<目标地址>:/data/archive/" \
  --apply

脚本会再次 dry-run,要求输入 APPLY,然后才正式同步;完成后再跑一次 dry-run。第二次结果为空,才说明目标端已经与计划一致。

真实截图:正式增量同步后,二次 dry-run 与 diff 均为空

三个平台的一键脚本

三套脚本都遵守同一套安全原则:默认只计划、默认不删除、正式执行前再次展示计划、同步后再次验证。区别只是操作系统外壳不同。

Windows、Ubuntu、macOS 使用同一套安全同步流程

Windows 11

Windows 11 脚本使用系统提供的 WSL 承载 rsync,不接入第三方同步服务。首次使用时,在管理员 PowerShell 中安装 WSL:

wsl --install

重启并完成 Ubuntu 初始化后,下载脚本:

生成计划:

powershell -ExecutionPolicy Bypass -File .\rsync-safe-sync-windows11.ps1 `
  -Source "/mnt/c/Data/source/" `
  -Destination "backup-user@<目标地址>:/data/archive/" `
  -Mode Plan

确认后执行,并在确实需要镜像删除时显式增加 -AllowDelete

powershell -ExecutionPolicy Bypass -File .\rsync-safe-sync-windows11.ps1 `
  -Source "/mnt/c/Data/source/" `
  -Destination "backup-user@<目标地址>:/data/archive/" `
  -Mode Apply -AllowDelete

Ubuntu 26.04

chmod +x rsync-safe-sync-ubuntu.sh
./rsync-safe-sync-ubuntu.sh "/data/source/" \
  "backup-user@<目标地址>:/data/archive/" --plan

脚本检测不到 rsync 时,只通过 Ubuntu 官方软件仓库安装 rsyncopenssh-client。正式执行:

./rsync-safe-sync-ubuntu.sh "/data/source/" \
  "backup-user@<目标地址>:/data/archive/" --apply

macOS 26

chmod +x rsync-safe-sync-macos.sh
./rsync-safe-sync-macos.sh "$HOME/Documents/source/" \
  "backup-user@<目标地址>:/data/archive/" --plan

macOS 系统自带的 rsync 版本可能较旧,但本文使用的基本 dry-run、itemize 和归档同步方式可先通过脚本实际测试。若你的任务需要新版本特性,再从可信包管理渠道升级,不要只因“版本号旧”就盲目替换系统工具。

人工自动执行方法

所谓“人工自动执行”,不是手工逐个复制文件,而是由脚本完成计算与同步,人只负责风险确认。

推荐顺序如下:

  1. 明确源端和目标端,尤其确认源路径末尾斜杠的语义。
  2. 先运行 --plan,把输出保存为带时间戳的计划文件。
  3. 单独检查所有 *deleting 行,确认没有目标端独有资料。
  4. 检查新增和修改数量是否符合预期;突然出现几万条变化时应停止。
  5. 运行 --apply,输入明确确认词。
  6. 查看同步后的第二次 dry-run;关键数据再抽样执行哈希或 diff 验证。

这种方式像飞机自动驾驶:大部分重复操作交给程序,但起飞、降落和异常处置仍需要人看仪表并作决定。

Agent 自动配置方法

Agent 可以帮助生成配置、运行 dry-run、归类差异并给出风险摘要,但不应该在没有授权时自行启用删除。可以把下面的约束直接交给本地 Agent:

目标:配置 rsync 增量同步。
要求:
- 只使用本机工具、SSH 和目标操作系统官方软件仓库;
- 首次只能执行 dry-run,不得修改文件;
- 使用 --itemize-changes 和结构化输出;
- 默认禁止 --delete;
- 汇总新增、修改、删除数量,并单独列出全部删除项;
- 等待我回复 APPLY 后才能正式同步;
- 正式同步后再次 dry-run,并报告是否为空;
- 日志中隐藏完整地址、账号、密钥和机器名。

Agent 自动化的价值是减少重复劳动,不是替人承担数据删除责任。一个合格的 Agent 流程必须允许中断、允许复核,并留下可读的计划文件。

如何改进原始“差异文件列表”脚本

如果只需要一个简洁的比较版本,可以这样写:

#!/usr/bin/env bash
set -euo pipefail

: "${LOCAL_DIR:?请设置 LOCAL_DIR}"
: "${REMOTE_DIR:?请设置 REMOTE_DIR}"

DIFF_FILE="${DIFF_FILE:-/tmp/rsync-diff-$(date +%Y%m%d-%H%M%S).txt}"
LOCAL_DIR="${LOCAL_DIR%/}/"

rsync --dry-run --archive --itemize-changes \
  --out-format='%i|%n%L' \
  "$LOCAL_DIR" "$REMOTE_DIR" \
  | tee "$DIFF_FILE"

echo "差异计划已保存:$DIFF_FILE"

如果确实需要检查镜像删除,在审核阶段额外加 --delete,但不要再把 deleting 行过滤掉。相反,可以单独提取:

grep '^\*deleting ' "$DIFF_FILE" || true

这时输出的目的不是“得到一份看起来干净的文件清单”,而是“得到一份能支持正确决策的风险清单”。

常见问题 Q&A

dry-run 会不会百分之百保证正式执行结果相同

不能。如果 dry-run 与正式执行之间有人修改了文件、网络挂载变化、权限变化或目标端空间不足,结果就可能不同。因此安全脚本会在正式执行前再次生成计划,并在执行后再验证。

--delete 会删除源端文件吗

通常是删除目标端中源端不存在的文件,而不是反过来删除源端。但源和目标写反后,后果同样严重。执行前必须把两端路径完整显示出来,并确认方向。

为什么“重命名”看起来像删除加新增

rsync 主要根据路径和文件状态工作,并不总能把两个不同路径自动理解为一次重命名。普通同步中,旧路径消失、新路径出现,往往就表现为删除旧文件和传输新文件。

为什么第二次 dry-run 仍有目录变化

可能是目录时间、权限、所有者、扩展属性或两端 rsync/文件系统能力不同。先看 %i 的具体属性位,再决定是否需要保留对应元数据;不要为了追求“绝对空白”随意增加高权限参数。

可以把脚本直接放进 cron 或计划任务吗

可以,但建议先连续观察多次计划输出。无人值守任务应默认禁用删除、设置日志保留、超时、锁和失败告警。涉及删除的无人值守同步最好配合目标端快照或版本化备份。

rsync 是不是备份工具

rsync 是强大的同步工具,但“镜像”不等于“备份”。如果误删或勒索软件加密已经发生,镜像同步可能把错误也同步过去。重要数据还需要历史版本、快照、离线副本或不可变备份。

最后的安全清单

真正安全的增量同步,不是一条参数很多的命令,而是一套可以解释、可以暂停、可以验证的流程。rsync 负责高效搬运文件,dry-run 负责提前亮出清单,而最终的风险判断,应该始终掌握在你手里。