如何使用Rsync仅复制特定子目录(多个目录中名称相同)

49

我在服务器1上有如下目录结构:

  • data
    • company1
      • unique_folder1
      • other_folder
      • ...
    • company2
      • unique_folder1
      • ...
    • ...

我想在服务器2上复制这个文件夹结构,但只复制unique_folder1下的目录/子目录。即结果应为:

  • data
    • company1
      • unique_folder1
    • company2
      • unique_folder1
    • ...

我知道rsync非常适合这个任务。 我已经尝试过使用'include/exclude'选项,但没有成功。

例如,我已经尝试过:

rsync -avzn --list-only --include '*/unique_folder1/**' --exclude '*' -e ssh user@server.com:/path/to/old/data/ /path/to/new/data/

但是,结果是我没有看到任何文件/目录:

receiving file list ... done
sent 43 bytes  received 21 bytes  42.67 bytes/sec
total size is 0  speedup is 0.00 (DRY RUN)

有什么问题?有想法吗?


额外信息: 我可以在两个服务器上使用sudo权限。我有一个想法-使用find命令和cpio一起复制到我需要的新目录中,然后使用Rsync。但这非常慢,文件很多等等。

4个回答

43

我找到了原因。对于我来说,不清楚Rsync是以这种方式工作的。
因此,company1目录的正确命令应该是:

rsync -avzn --list-only --include 'company1/' --include 'company1/unique_folder1/***' --exclude '*' -e ssh user@server.com:/path/to/old/data/ /path/to/new/data

即我们需要包含每个父级company目录。当然,我们不能在命令行中手动编写所有这些company目录,所以我们将列表保存到文件中并使用它。


我们需要完成的最后几件事:

1. 在服务器1上生成包含文件,因此其内容将为(我使用了lsawk):

+ company1/  
+ company1/unique_folder1/***  
...  
+ companyN/  
+ companyN/unique_folder1/***  

2.将 include.txt 复制到服务器 2 并使用以下命令:

rsync -avzn                                        \
      --list-only                                  \
      --include-from '/path/to/new/include.txt'    \
      --exclude '*'                                \
      -e ssh user@server.com:/path/to/old/data/    \
      /path/to/new/data

嘿,Andron,你使用三个星号的原因是什么?我尝试过使用两个和三个星号,但我看不出有什么区别。现在我正在使用这种技术来备份一些文件,感谢你发布它。 - Chad von Nau
5
没事了,我已经想通了。我之前使用的是folder**,而不是folder/***。当你在目录名后面加上斜杠时,需要第三个星号。两个星号和没有斜杠的方法也可以工作,但不太准确,因为它还会匹配具有相同基础名称的同级文件夹。 - Chad von Nau
@ChadvonNau 嗯,我不确定为什么我使用了 ***。在 RSync 文档 中,我看到 use '**' to match anything, including slashes。所以也许两个星号就足够了。但我认为三个更好 :) - Andron
1
如果您想要排除最顶层目录,请考虑此链接http://unix.stackexchange.com/a/42691/37431 - rofrol
这里是用于测试的 -n--list-only 吗?我是 rsync 新手,不知道为什么这个命令只列出而不执行任何操作。 - Kagami Sascha Rosylight
5
关于三个星号;Rsync手册定义...以"dir_name/***"结尾将匹配目录(就像指定了"dir_name/"一样)以及目录中的所有内容(就像指定了"dir_name/**"一样)。此行为在版本2.6.7中添加。 - Dogsbody

33
如果第一个匹配的模式排除了一个目录,那么它的所有后代都不会被遍历。当您想要包含一个深层目录时,例如 company*/unique_folder1/**,但排除其他所有内容*,您需要告诉rsync也包括其所有祖先:
rsync -r -v --dry-run                       \
    --include='/'                           \
    --include='/company*/'                  \
    --include='/company*/unique_folder1/'   \
    --include='/company*/unique_folder1/**' \
    --exclude='*'

你可以使用Bash的花括号扩展来减少打字量。花括号扩展后,以下命令与先前的命令完全相同:

rsync -r -v --dry-run --include=/{,'company*/'{,unique_folder1/{,'**'}}} --exclude='*'

2
谢谢 @yonran,正如你下面所看到的 - 'include list' 太大了。这就是为什么将列表放在一个文件中(请参见下面的接受答案)。还有感谢 "bash 的花括号扩展" - 需要试一试。 - Andron
这个答案是正确的,但是如果我们使用bash特性,那么就会进入一个灰色地带 :) 在这种情况下,值得注意的是,一个简单的 shopt -s globstar; rsync -avn --relative /sourcepath/./**/a destpath 就可以了。 - Marcus

8

除了 Andron's Answer,在许多情况下更容易理解和实现的替代方法是使用--files-from=FILE选项。对于当前问题,

rsync -arv --files-from='list.txt' old_path/data new_path/data

其中list.txt只是简单的

company1/unique_folder1/
company2/unique_folder1/
...

请注意,必须显式包含-r标志,因为--files-from会关闭-a标志的此行为。对我来说,路径构建似乎与其他rsync命令不同,在其中company1/unique_folder1/匹配,但/data/company1/unique_folder1/却不匹配。

1
这种方法对我来说更容易,因为它允许使用“find”生成要包含的目录列表。 - Sam R

3
例如,如果您只想将 target/classes/target/lib/ 同步到远程系统,请执行以下操作:
rsync -vaH --delete --delete-excluded --include='classes/***' --include='lib/***' \
      --exclude='*' target/ user@host:/deploy/path/

重要的注意事项:
  • 不要忘记路径结尾处的"/",否则你将得到一个复制品存放在子目录中。
  • --include--exclude 的顺序很重要。
  • 与其他答案相反,以"/"开头的 include/exclude 参数是不必要的,它们会自动附加到源目录(target/ 在这个例子中)。
  • 为了测试确切的操作,我们可以使用--dry-run标志,就像其他答案所说。
  • --delete-excluded将删除目标目录中的所有内容,除了我们特别包含的子目录。它应该谨慎使用! 出于这个原因,--delete 是不够的,它默认情况下不会删除远程端被排除的文件(其他每一个都会),需要再次提供--delete

谢谢。那是个好主意。但在我的情况下,你可以看到我在X个不同的目录中有相同的子目录名称。所以,我不确定你的想法是否可行。 - Andron
@Andron 是真的。我认为 --include 参数列表应该被修改,可能改为类似于 --include='***/dirName/' 这样的格式。虽然我没有测试过,但我的例子来自一个经过验证、测试和工作的部署脚本。 - peterh
1
这个回答实际上并没有回答问题。建议使用 --include='***/dirName/' 并不能按预期工作。 - Marcus
@Marcus,我在2019年初开始使用它,现在仍在使用,并且它按预期工作。您能否请解释一下,您遇到了什么问题? - peterh
示例如下,未同步任何内容: mkdir -p data/company{1,2}/{unique_folder1,other_folder}; touch data/company{1,2}/{unique_folder1,other_folder}/testfile; tree data; rsync -vaH --include='***/unique_folder1/' --exclude='*' data/ dest``` - Marcus
我使用上面修改后适用于我的目录的确切示例也无法同步任何内容。有什么想法吗? - Joseph Astrahan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接