登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  linux

Linux rsync 怎样用 --files-from 只同步指定文件清单

来源:17golang原创

时间:2026-10-26 05:02:22 453浏览 收藏

当源目录很大、但本次只想把几份配置和一个子目录同步到目标端时,rsync -a 源目录/ 目标目录/ 过于宽泛。更合适的做法是使用 --files-from=清单文件:rsync 只读取清单中的相对路径,路径仍以源目录为基准。

官方地址:https://download.samba.org/pub/rsync/rsync.1.html

这次问题的关键不是把 --include 换成另一个写法,而是先确定清单的边界,再分别处理文件、目录和递归。下面按一次“同步范围被意外放大”的故障复盘来拆开。

先把影响面锁定在源目录

最容易出现的症状是:清单里写了一个文件,结果目标端多出了一层目录,或者写了目录名却没有得到预期的子文件。原因通常不是 rsync 随机选择,而是清单条目始终相对源目录解析。

# 清单中的路径相对 /srv/app,不要写成目标端的绝对路径
cat > /tmp/rsync-files.txt 

如果清单中写 config/app.yaml,rsync 会从 /srv/app/config/app.yaml 查找,并在目标端对应位置创建 config/。清单开头的斜杠会被移除,不能借此跳到源目录之外;无法折叠的 .. 也会被拒绝。这个限制很重要:清单是受控输入,不是任意路径复制器。

源目录、files-from 清单、相对路径和目标目录的静态边界关系
图1:结构说明图展示源目录基准与清单路径的静态关系,不是终端截图或实际运行证据。

目录行不等于递归复制

第二个症状是清单里写了 logs,目标端只有一个空目录;或者把 logs/ 和 -r 混在一起后,复制范围突然扩大。这里要分清三个语义:

  • 单个文件行只选中这个文件。
  • 目录行会让目录本身进入文件列表;--dirs 默认会创建清单中出现的目录。
  • --files-from 不会让 -a 自动带上递归,若要展开目录层级,必须显式加入 -r。
# 清单中的目录行只表达目录路径,不自动承诺整棵树
cat > /tmp/rsync-files.txt 

目录尾部的斜杠也有含义:官方手册把 reports/ 解释为目录的直接内容可以进入传输;而没有尾部斜杠的目录条目首先表达目录本身。不要把“目录被创建”和“目录内容已经递归复制”当成同一件事。

清单目录条目、dirs、recursive 和 delete 的静态边界关系
图2:结构说明图区分目录条目、递归展开和删除范围,不是界面截图或运行结果。

把清单同步命令缩到可审阅范围

一次排障中,最稳妥的修复动作是先保留最少选项,再按结果增加能力。下面的命令适合把明确列出的文件和目录送到远端;命令块中的注释说明了每个关键开关的作用。

# 清单按路径排序,减少 rsync 反复扫描共同的父目录
LC_ALL=C sort -u /tmp/rsync-files.txt > /tmp/rsync-files.sorted

# -a 保留归档属性;-r 显式允许清单中的目录递归展开
# --files-from 只传送清单列出的路径;--dry-run 只用于人工预览
rsync -a -r --dry-run +  --files-from=/tmp/rsync-files.sorted +  /srv/app/ backup.example:/backup/app/

--files-from 会隐含 --relative 和 --dirs,所以清单中的路径层级可以在目标端保留,列出的目录也不会被静默跳过;但递归仍要单独写出。排序不是语法要求,而是官方手册给出的效率建议:相邻条目共享父路径时,rsync 可以少做重复扫描。

示例中的 --dry-run 只用于在变更前查看传输清单;正式执行时去掉它。不要把 dry-run 的文字输出当作真实传输回执,也不要将未列出的兄弟文件推断为会被删除。

删除和特殊文件名要单独判断

事故复盘里最危险的误判是:“既然清单代表完整范围,加上 --delete 就会删除目标端所有未列出的文件。”这不成立。只列出单个文件时,未列出的兄弟项没有进入完整目录同步范围,--delete 不会替你建立这个删除语义;只有把目录本身纳入并显式递归,才可能形成不同的删除范围。

# 普通换行清单适合没有换行符的路径
rsync -a -r --files-from=/tmp/rsync-files.sorted /srv/app/ /backup/app/

# 路径可能包含换行符时,用 NUL 分隔并配合 --from0
# printf 的 %s\0 不会把路径按空格或换行错误拆开
printf '%s\0' 'config/app.yaml' 'reports/2026/summary.csv' +  > /tmp/rsync-files.nul
rsync -a --from0 --files-from=/tmp/rsync-files.nul +  /srv/app/ /backup/app/

--from0 同时改变 --files-from 等清单类输入的分隔方式。路径来自脚本或用户输入时,优先明确采用换行还是 NUL;不要一边生成 NUL 清单,一边忘记传 --from0。如果确实要做镜像删除,应先定义完整目录的同步边界,再单独评估 --delete,不要把清单模式当成无条件删除授权。

复盘结论与常见疑问

这次“同步范围不对”的根因可以归结为三个判断错位:把清单路径当成目标路径,把目录创建当成递归复制,把列出文件当成完整目录镜像。修复后的检查顺序是:确认源目录、逐行审阅清单、决定是否需要 -r,再确认路径分隔符和删除范围。

--files-from 能不能读取远端清单?可以,官方手册支持在参数中带远端主机前缀,或用 : 简写请求传输另一端的清单;但主机必须匹配传输的一端。

为什么用了 -a 目录内容仍不全?--files-from 改变了归档选项的默认行为,递归不会因为 -a 自动开启;需要明确加入 -r,并检查清单里是否真的列出了目录或文件。

能否用 --files-from 代替 include-from?不能简单等同。--include-from 提供的是过滤规则,--files-from 提供的是要传输的具体路径清单;两者的输入语义和递归边界不同。

把 --files-from 看成“受控文件列表”而不是“缩小版全量同步”,大多数路径层级、目录递归和误删疑问都能在命令执行前被发现。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>