在局域网里传文件,scp 虽然简单,但一旦涉及目录、软链接、增量同步、所有权保留,rsync 才是更趁手的工具。这篇文章把几个最容易踩坑的点讲清楚:路径末尾的斜杠、软链接处理、压缩机制、dry-run 的用法,以及文件属主与工作组的映射机制。
局域网里最常用的一条:
-a:归档模式,保留权限、时间、软链接等
-v:显示传输过程
这是 rsync 最容易踩的坑,源路径末尾有没有 /,语义完全不同。
| 写法 | 效果 |
|---|---|
rsync -av source/ dest/ | 把 source 里面的内容复制到 dest/ |
rsync -av source dest/ | 把 source 这个目录本身复制到 dest/,即 dest/source/ |
举个例子,本地有 fab/ 目录,里面有 a.txt、b.txt:
记忆口诀:源带斜杠 = 复制内容;源不带斜杠 = 复制目录本身。
目标路径末尾的斜杠一般不影响结果,但建议也带上,语义更清晰。
rsync 只允许目标路径的最后一级不存在,会自动创建它。但更上层的目录必须已经存在,否则报错。如果需要像 mkdir -p 一样自动创建所有层级,加 --mkpath:
scp -r 在处理软链接时会跟随链接,把真实文件或目录的内容复制过去。这带来两个问题:
软链接指向的内容被当成普通文件复制,语义丢失
如果链接指向目录树中的其他位置,可能导致数据重复传输
而 rsync 的 -a 选项默认包含 -l,会保留软链接本身,只复制链接,不跟随:
这样软链接在目标端依然是一个软链接,指向关系保持不变(前提是目标端对应路径也存在)。
如果你确实想跟随软链接,可以用 -L:
小结:
| 需求 | scp | rsync |
|---|---|---|
| 保留软链接 | 不支持 | -a(默认含 -l) |
| 跟随软链接 | 默认行为 | -L |
-z 更高效很多人会想:是不是先手工 tar czf 打包,传过去再解压更好?
在局域网里,直接用 rsync 的 -z 更省事,通常也更高效。
原因在于:
-z 是传输层动态压缩,数据边传边压、边解边写,不落地中间压缩包
手工压缩需要额外一次打包和一次解包,还要占用磁盘存临时文件
-z 会自动跳过已压缩的文件(如 .jpg、.mp4、.zip),避免做无用功
不过要注意:局域网带宽通常很充足,-z 的压缩/解压反而会消耗 CPU。如果两台机器 CPU 较弱、网络又是千兆局域网,去掉 -z 可能更快:
结论:
局域网高速网络:-av 即可,-z 可选
跨公网或慢速网络:-avz 明显更划算
手工压缩再传:只在网络极差、且需要断点续传单个大包时才有优势,日常不推荐
--dry-run(可简写 -n)让 rsync 只模拟传输过程,打印会做什么,但不实际改动任何文件。
它的价值在于:
确认路径斜杠带来的实际效果是否符合预期
检查 --delete 会删掉哪些文件,避免误删
验证 --exclude 排除规则是否生效
传输大批量文件前,先看一眼文件列表对不对
强烈建议:任何带 --delete 的操作,先 dry-run 一遍。
rsync -a 中的 -o(owner)和 -g(group)负责保留文件的所有者和所属组。但它的工作方式并不是简单地把源端的数字 ID 原样搬过去。
rsync 默认会使用用户名和组名在目标端进行映射。具体流程是:
源端发送文件的用户名/组名(而不是数字 UID/GID)。
接收端用这个名字去查找本地的用户/组,找到对应的数字 ID,然后设置所有权。
这意味着:如果两台机器上有一个同名的用户(比如都叫 user),即使它们的数字 UID 不同,rsync 也能正确映射,文件在目标端仍归 user 所有。
如果源端的用户名或组名在目标端不存在,rsync 会回退到使用源端的数字 UID/GID 来设置所有权。这带来两个后果:
如果目标端恰好有个用户占用了这个数字 UID,文件会“错误地”归那个用户所有。
如果没有任何用户占用这个数字 UID,ls -l 会显示一个纯数字的所有者,这在大多数场景下可以接受,不影响文件读写(权限位仍然保留)。
-o(保留所有者)要生效,接收端的 rsync 必须以 root 身份运行(或使用 --fake-super)。否则,接收端没有权限把文件 chown 给别的用户,rsync 只能把新文件的所有者设为当前发起传输的那个用户。
分情况:
情况 A:目标是保留源端的所有权语义(如备份场景)
远端有同名用户/组:默认即可,rsync 自动按名映射,无需后处理。
远端没有同名用户/组:备份时文件归数字 ID 所有没问题,恢复时用 --chown 修正:
情况 B:希望文件在目标端统一归某个用户所有
用 --chown=USER:GROUP 强制指定,最简洁:
情况 C:多种用户需要分别映射
用 --usermap 和 --groupmap,支持通配符:
比如 --usermap=*:nobody 可以把所有源端用户都映射为 nobody。
--numeric-ids如果你不想按名字匹配,而是原样传输数字 UID/GID,用:
这在两端 UID/GID 已经通过 LDAP/NIS 统一管理的场景下有用,可以避免名字匹配带来的意外。
属主映射小结:
| 场景 | 建议 |
|---|---|
| 远端有同名用户/组 | 默认即可,无需后处理 |
| 远端无同名用户/组,备份用 | 备份时接受数字 ID,恢复时用 --chown 修正 |
| 希望目标端统一归某个用户 | --chown=user:group |
| 需要复杂映射 | --usermap + --groupmap |
| 两端 UID/GID 已统一管理 | --numeric-ids |
| 选项 | 作用 |
|---|---|
-a | 归档模式,保留权限、时间、软链接等 |
-v | 显示详细过程 |
-z | 传输时压缩 |
-P | 显示进度 + 支持断点续传 |
--delete | 目标端删除源端没有的文件,保持完全一致 |
--exclude='*.log' | 排除指定文件 |
--dry-run | 只模拟,不实际传输 |
--mkpath | 自动创建目标端缺失的中间目录 |
-L | 跟随软链接(默认是保留) |
--chown=user:group | 强制指定目标端所有者/组 |
--usermap / --groupmap | 按名映射用户/组 |
--numeric-ids | 原样传输数字 UID/GID |
局域网环境下,-av 已经能满足绝大多数需求;需要看进度就加 -P,需要压缩就加 -z。掌握路径斜杠、软链接、属主映射这三个关键点,基本就不会再踩坑了。
分享数字集成电路设计中的经验和方法。分享让工作更轻松。