这似乎是一个非常简单的请求,但我已经为 rsync 绞尽脑汁大约一个小时了,所以我想问一下。我只是想使用 rsync 将本地目录中的一小组文件列入远程目录中的白名单。
假设现在我只想发送*.ts
根目录中的文件。因此,想象一下源结构如下:
/foo
bar.ts
baz.ts
package.json
node_modules/...
other_dir/...
在这个例子中,我只想发送bar.ts
和baz.ts
。
我是谁试要做的就是:
rsync --include-from include.txt foo remotehost:foo
其中include.txt
仅包含:
*.ts
当我尝试这样做时,我明白了skipping directory .
为什么如果我有一个include-from
列表,会发生这种情况?
因此我尝试使用存档/递归模式(但这不是我想要的,因为我只是想指定一个列表……但无论如何让我们尝试一下):
rsync -a --include-from include.txt foo remotehost:foo
这样就可以复制所有内容foo
并忽略include-from
。
于是我尝试:
rsync -a --exclude '*' --include-from include.txt foo remotehost:foo
我以为这些模式是从左到右进行评估的,所以我希望这只包含我列表中的文件,但它排除了它们,因为它们与模式“*”匹配
再说一次,这看起来是一个非常基本的事情,我不知道为什么让 rsync 做到这一点如此具有挑战性。
我在这里遗漏了什么?
答案1
让我从最简单的解决方案开始,然后解释一下为什么你的尝试没有达到预期效果。试试这个:
rsync foo/*.ts remotehost:foo
请注意,shell 在运行之前会扩展通配符rsync
,因此这基本上等同于:
rsync foo/bar.ts foo/baz.ts remotehost:foo
编辑:经过评论中的讨论,我认为根本问题是规则名称“包括”有点误导;称它们为“不要排除“规则,甚至更好”即使过滤列表后面有一条排除规则,也不要排除“(但这些都很冗长,而且对 shell 脚本语法不友好,所以”排除“ 这是)。
这样说吧:告诉-r
在源目录内传输文件/子文件夹,“排除”规则允许您对此做出例外(即跳过一些将被传输的项目),而“包含”规则允许您对例外做出例外(即传输以后的“排除”规则将排除的项目)。-a
rsync
现在,让我们回顾一下那些不起作用的命令:
rsync --include-from include.txt foo remotehost:foo
这里的问题是你告诉它同步目录(foo),而不是文件在该目录。使用-r
(或-a
) 选项,它不会执行此操作,因此它只会跳过该目录。因此,您添加-a
:
rsync -a --include-from include.txt foo remotehost:foo
...现在它会发送所有内容,因为你给了它一份要包含的内容列表,但没有告诉它要包含哪些内容排除任何东西。默认是包含事物,添加明确的指令来包含特定事物不会改变这一点。
因此你添加一个排除指令:
rsync -a --exclude '*' --include-from include.txt foo remotehost:foo
...但是您首先放置了排除规则,它会从左到右检查规则,并根据第一个匹配项采取行动。由于所有内容都匹配*
,因此所有内容都会被排除,并且文件中的包含规则永远不会被应用。
您应该能够通过首先放置包含规则来使其工作:
rsync -a --include-from include.txt --exclude '*' foo remotehost:foo
...但正如我在开头所建议的那样,使用 shell 通配符进行匹配会更容易。尽管你会如果您要将文件从远程复制到本地,则需要使用类似这样的方法,因为您的本地 shell 无法应用远程通配符。
答案2
非常感谢 Gordon Davisson 的详细回复。
我最终选择了以下方向,其行为更接近我的预期并且感觉“优雅”:
rsync -aF foo remotehost:foo
作为a
存档标志(以递归方式处理foo
),并F
指示 rsync 它应该.rsync-filter
在它检查的每个目录中查找名为的文件。
然后我的.rsync-filter
文件最终指定了包含/排除规则:
+ **/*.ts
- node_modules
我喜欢这个解决方案,因为 rsync 已经为这个“过滤器”指定了预期的文件名,所以我不需要编造(并指定)任意的包含/排除文件名。它也是一个简短的单字母标志,因此发出的命令非常易读。