Rsync过滤器:仅复制一种模式


128

我正在尝试创建一个目录,该目录将仅容纳从LaTeX编译的所有PDF。我喜欢将每个项目都放在一个单独的文件夹中,所有项目都放在一个名为的大文件夹中LaTeX。所以我尝试运行:

rsync -avn *.pdf ~/LaTeX/ ~/Output/

它将在其中找到所有pdf ~/LaTeX/并将其传输到输出文件夹。这行不通。它告诉我找不到与“ *.pdf” 匹配的内容。如果我不使用此过滤器,该命令将列出LaTeX下所有项目文件夹中的所有文件。因此,*。pdf过滤器存在问题。我尝试用~/主目录的完整路径替换,但这没有任何效果。

我正在使用zsh。我尝试在bash中执行相同的操作,甚至使用列出了每个子目录中每个文件的过滤器进行操作...这是怎么回事?

为什么rsync无法理解我的仅pdf过滤器?


好。所以更新:不,我正在尝试

rsync -avn --include="*/" --include="*.pdf" LaTeX/ Output/

这给了我整个文件列表。我猜是因为一切都与第一种模式相符...


嗯,您似乎是对的...我想我的回答(使用zsh **模式)应该可以。
Marcel Stimberg 2010年

Answers:


249

TL,DR:

rsync -am --include='*.pdf' --include='*/' --exclude='*' ~/LaTeX/ ~/Output/

Rsync将源复制到目标。如果您*.pdf作为源传递,则Shell会将其.pdf扩展为当前目录中带有扩展名的文件列表。没有递归遍历发生,因为您没有将任何目录作为源传递。

因此,您需要运行rsync -a ~/LaTeX/ ~/Output/,但是要有一个过滤器来告诉rsync .pdf仅复制文件。阅读手册时,Rsync的过滤器规则似乎令人生畏,但是您可以仅用几个简单的规则来构造许多示例。

  • 包含和排除:

    • 不包括通过名称或位置中的文件是简单的:--exclude=*~--exclude=/some/relative/location(相对于源参数,例如这排除~/LaTeX/some/relative/location)。
    • 如果您只想匹配几个文件或位置,请包括它们,包括指向它们的每个目录(例如,使用--include=*/),然后使用排除其余文件或位置--exclude='*'。这是因为:
    • 如果排除目录,则排除目录下的所有内容。排除的文件将不会被考虑。
    • 如果包含目录,则不会自动包含其内容。在最新版本中,--include='directory/***'将这样做。
    • 对于每个文件,将应用第一个匹配规则(并且包括所有未匹配的内容)。
  • 模式:

    • 如果某个模式不包含/,则该模式适用于文件名sans目录。
    • 如果模式以结尾/,则仅适用于目录。
    • 如果模式以开头/,它将适用于作为参数传递到目录的目录的整个路径rsync
    • *单个目录组件的任何子字符串(即永远不匹配/);**匹配任何路径子字符串。
  • 如果源参数以结尾,则将/复制其内容(为每个rsync -r a/ b创建)。否则,目录本身将被复制(创建)。b/fooa/foorsync -r a bb/a


因此,这里我们需要包括*.pdf,包含包含它们的目录,并排除其他所有内容。

rsync -a --include='*.pdf' --include='*/' --exclude='*' ~/LaTeX/ ~/Output/

请注意,这会复制所有目录,即使是不包含匹配文件的目录或包含一个目录的子目录。使用--prune-empty-dirs选项可以避免这种情况(这不是通用解决方案,因为即使显式匹配,您也无法复制目录,但这是很少的要求)。

rsync -am --include='*.pdf' --include='*/' --exclude='*' ~/LaTeX/ ~/Output/

与我的解决方案(使用zsh的**模式)相比,这将在目标目录中重新创建目录结构。我不确定这是否是OP想要的...
Marcel Stimberg 2010年

我只想包含一个目录,而排除/etc/lsyncd/lsyncd.conf.lua文件中所有目录的其余部分。有什么想法吗?
Dhaduk Mitesh

@DhadukMitesh我对lsyncd不熟悉。您应该提出这个新问题。
吉尔斯

25
rsync -av --include="*/" --include="*.pdf" --exclude="*" ~/Latex/ ~/Output/ --dry-run

默认设置为包括所有内容,因此包含要传输的文件之后,您必须明确排除所有内容。删除--dry-run以实际传输文件。

如果您开始使用:

--exclude '*' --include '*.pdf'

然后贪婪的匹配将立即排除一切。

如果你试试:

--include '*.pdf' --exclude '*' 

然后,仅顶层文件夹中的pdf文件将被传输。它不会跟随任何目录,因为这些目录由“ *”排除。


2
截至2014-03-17,这是最好的答案,因为它可以完全解决原始海报问题。请投票!如果添加--prune-empty-dirs(或快捷方式-m),那么您甚至可以在目的地保留许多空目录,当然,您当然希望它们作为提醒或结构蓝图。
porg 2014年

1
最佳答案,--include =“ * /”是关键。
Martin Konicek

我只想包含一个目录,而排除/etc/lsyncd/lsyncd.conf.lua文件中所有目录的其余部分。有什么主意吗
Dhaduk Mitesh

15

如果您使用类似的模式*.pdf,则外壳程序会“扩展”该模式,即它将模式替换为当前目录中的所有匹配项。您正在运行的命令(在本例中为rsync)没有意识到您尝试使用模式的事实。

但是,在使用zsh时,有一个简单的解决方案:**模式可用于递归匹配文件夹。尝试这个:

rsync -avn ~/LaTeX/**/*.pdf ~/Output/

那不是复制所有PDF从某处在当前目录中到〜/输出〜/乳胶/一切吗?
SamB

我想您的意思是rsync -avn ~/LaTeX/**/*.pdf ~/Output,但是--include无论如何,解决方案都具有更大的可扩展性。
亚当·伯瑞克

抱歉,更正了我匆忙输入的命令...我同意include命令(在SamB的版本中)更好,尽管rsync更加复杂且特定于rsync,而**在其他情况下也可能会派上用场。
Marcel Stimberg 2010年

1
Bash 4也采用了相同的功能。哦,您这里不需要rsync,cp可以。在某些系统上,如果有很多文件,则cd ~/Latex && cp -p **/*.pdf ~/Output可以避免“命令行太长”错误。
吉尔斯

1
请注意,包含和排除过滤器中使用的rsync模式也具有执行相同功能的**。您可以通过将其他shell引号引起来来将*转义。
丹·普里茨

13

您可以使用find和中间文件列表(files_to_copy)解决问题。确保您位于主目录中,然后:

find LaTeX/ -type f -a -iname "*.pdf" > files_to_copy && rsync -avn --files-from=files_to_copy ~/ ~/Output/ && rm files_to_copy

经过Bash测试。


我认为find是最可靠的解决方案,但是我会选择使用finds -exec选项或using xargs。像这样的东西:find LaTeX/ -type f -iname "*.pdf" -print0 | xargs -0 -i rsync -avn {} Output/
Steven D

是的...我建议也找到...虽然我认为rsync 必须能够做到这一点。
gabe。

这也是解决难题的一个很好的解决方案:大概我可以使用它来排除文档类为standalone或不具有.tex相同名称的文件的文件,因为这些将是某些文档中包含的图像...
Seamus

2
rsync选项--files-from接受从stdin读取。这将起作用 find LaTeX/ -type f -a -iname "*.pdf" | rsync -avn --files-from=- ~/ ~/Output/
Juan Calero 2012年

9

根据联机帮助页的“包含/排除模式规则”部分判断,执行此操作的方法是

rsync -avn --include="*/" --include="*.pdf" ~/Latex/ ~/Output/

--include="*/"标志与kbrd的答案之间的关键区别是标志,该标志告诉rsync继续复制找到的任何目录,无论它们如何命名。这是必需的,因为除非已指示rsync复制该子目录,否则它不会递归到该子目录中。

另外,请注意,引号会阻止外壳程序尝试将模式扩展为相对于当前目录的文件名,并执行以下操作之一:

  1. 成功并弄乱过滤器(不太可能出现在这样的标志中间,尽管您真的不知道何时有人会创建一个名为--include=foo.pdf... 的文件)

  2. 失败,并且有可能产生错误而不是运行命令(就像您发现的zsh在默认情况下一样)。


因此,这只会复制PDF和目录结构,而kbrd会复制文件,但忽略结构?
Seamus 2010年

1
嗯 我想这实际上似乎仍然可以尝试复制所有内容,因为那是没有过滤器的结果,因此include在其中添加多余的内容不会改变任何内容。如果您明白我的意思...
Seamus

7
您需要--exclude="*"在之后--include="*.pdf",否则将转移所有内容。
jmanning2k 2010年

@ jmanning2k:啊。很高兴知道!
SamB

4

这个怎么样:

rsync -avn --include="*.pdf" ~/Latex/ ~/Output/

不,man rsync将过滤器放在选项之后和源/目标之前。我尝试了一下,但没有成功
Seamus 2010年

您可以按照自己的方式在当前文件夹中找到.pdf文件,但不能以递归方式找到。(该a选项用于归档和除其他事项外它使复制递归。
西莫

1
糟糕,我的糟糕。我更新了答案。
kbyrd

+1太接近了,为我提供了有关如何在手册页中找到相关资料的线索。(希望我什至没错。:-)
SamB

3

这是一些无需使用find即可工作的东西。与已经发布的答案的区别在于过滤规则的顺序。rsync命令中的过滤器规则与iptable规则非常相似,文件匹配的第一个规则是所使用的规则。从手册页

构建要传输的文件/目录列表后,rsync会根据包含/排除模式的列表依次检查要传输的每个名称,然后执行第一个匹配模式:如果是排除模式,则该文件为跳过 如果是包含模式,则不跳过该文件名;如果找不到匹配的模式,则不跳过文件名。

因此,您需要以下命令:

rsync -avn --include="**.pdf" --exclude="*" ~/LaTeX/ ~/Output/

注意“ **。pdf”模式。根据手册页

如果模式包含/(不计入结尾的/)或“ **”,则它将与完整路径名(包括任何前导目录)匹配。如果模式不包含/或“ **”,则仅与文件名的最后部分匹配。(请记住,算法是递归应用的,因此“完整文件名”实际上可以是从起始目录向下的路径的任何部分

在我的小型测试中,这确实在目录树下递归起作用,并且仅选择pdf。


您如何测试?根据我对文档的理解和实验验证,您的命令应仅*.pdf在顶层目录中复制(而不能在~/LaTeX/foo/bar.pdf)中复制。
吉尔斯(Gilles)2010年

@吉尔斯·克鲁德 你是对的。我发誓我测试了它,但它确实有效,但是我似乎无法重新创建它。现在,我实际上已经阅读了引用的手册页,这很有意义,这是行不通的。叽。
史蒂文D

1
好吧,我弄清楚了我的测试错了。我的“小测试”在具有我自己的.tex和.pdf文件的目录上。然后,我在该子目录中创建了一个“ test”子目录以及一个test.pdf和test.tex。但是,我没有注意到我的顶级目录中有一个test.pdf,这可能是因为我做了一些快速的LaTeX实验。
史蒂文D

我还是不明白**。举个例子会很好。;)
buhtz

2

这是我的首选解决方案:

find source_dir -iname '*.jpg' -print0 |  rsync -0 -v --files-from=- . destination_dir/

find命令比rsync:-) 的包含/排除规则更容易理解。

如果您只想复制pdf文件,只需更改.jpg.pdf

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.