it-swarm.cn

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

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

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

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

我正在使用zsh。我试图在bash甚至with列出每个子目录中每个文件的过滤器中做同样的事情……这是怎么回事?

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


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

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

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

142
Seamus

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/foo创建a/foo)。否则,将复制目录本身(rsync -r a b创建b/a)。


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

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

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

rsync -am --include='*.pdf' --include='*/' --exclude='*' ~/LaTeX/ ~/Output/
274
rsync -av --include="*/" --include="*.pdf" --exclude="*" ~/Latex/ ~/Output/ --dry-run

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

如果您开始使用:

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

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

如果你试试:

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

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

30
jmanning2k

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

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

rsync -avn ~/LaTeX/**/*.pdf ~/Output/
15
Marcel Stimberg

您可以使用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测试。

13
Derek Frye

通过 manpage 的“ INCLUDE/EXCLUDE PATTERN RULES”部分判断,做到这一点的方法是

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

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

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

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

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

9
SamB

这是我的首选解决方案:

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

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

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

3
guettli

这个怎么样:

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

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

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

因此,您需要以下命令:

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

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

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

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

2
Steven D

要从源目录内部生成仅包含标题(../include)的目录,请执行以下操作:

rsync -avh --Prune-empty-dirs --exclude="build" --include="*/" --include="*.h" --exclude="*" ./* ../include/

这不包括所有空目录和目录build

0
SCG82