使用包含选项,仅复制特定类型的文件的rsync副本

195

我使用以下bash脚本仅复制特定扩展名的文件(在此示例中为*.sh),但它仍会复制所有文件。有什么问题吗?

from=$1
to=$2
rsync -zarv --include="*.sh" $from $to

7
虽然并非直接相关,我建议引用$from/$to。如果第一/二个位置参数包含空格,则不这样做可能会导致意外结果。 - Kjetil Joergensen
你明白为什么你的命令不能正常工作了吗? - Charlie Parker
@CharlieParker:你必须使用rsync吗?这完全可以通过shell内部实现。 - Inian
这个问题及其答案的不足之处在于,如果我有递归目录想要仅发送一种类型的文件,如何编写命令也没有提到。似乎它只对目标目录有效... - Charlie Parker
旁注:因为“-a”意味着“-r”,所以“-r”是多余的。 - wisbucky
6个回答

325

我认为--include是用于包含一些被--exclude排除的文件的子集,而不仅仅是仅包含那些文件。

换句话说:你需要将包含理解为不排除

请尝试:

rsync -zarv  --include "*/" --exclude="*" --include="*.sh" "$from" "$to"

对于rsync版本3.0.6或更高版本,顺序需要按照以下方式进行修改(请参见注释):

rsync -zarv --include="*/" --include="*.sh" --exclude="*" "$from" "$to"

加上 -m 标志将避免在目标目录中创建空的文件夹结构。在版本 3.1.2 中测试过。

因此,如果我们只需要 *.sh 文件,则必须排除所有文件 --exclude="*",包括所有目录 --include="*/" 并包括所有 *.sh 文件 --include="*.sh"

您可以在 man 页面Include/Exclude Pattern Rules 部分找到一些好的示例。


11
如果你想同步所有子目录,但是如果子目录中有任何.sh文件需要同步,那么你很可能也需要使用--include="*/"。 - Kjetil Joergensen
62
我在使用我很久以前从macports下载的rsync版本3.0.7时尝试过这个包含/排除的顺序,但它并没有起作用。最终我采用了以下方式(已适应OP):rsync -zarv --include="*/" --include="*.sh" --exclude="*" "$from" "$to" - Bijou Trouvaille
4
我尝试过使用rsync 3.0.9,但没有成功。Bijou是正确的,顺序不正确(先使用--include=\*.sh,然后再使用--exclude=\*)。 - TrueY
3
你的include/exclude顺序不起作用,但按照Bijou Trouvaille建议的顺序可以起作用。 - John Smith Optional
4
为什么我们需要这么多的包含(includes)命令,它看起来很傻。 - Charlie Parker
显示剩余13条评论

95

@chepner的答案将复制所有子目录,无论其中是否包含文件。如果您需要排除不包含该文件的子目录,并仍保留目录结构,请使用

rsync -zarv  --prune-empty-dirs --include "*/"  --include="*.sh" --exclude="*" "$from" "$to"

4
这是我的要求:“如果你需要排除不包含该文件的子目录,但仍保留目录结构” +1 - Juuso Ohtonen
2
我不明白你是怎么知道--includes的顺序的。 - Charlie Parker
1
如果我有递归目录,并且只想发送一种类型的文件,该如何编写命令?似乎它只对目标目录执行此操作。 - Charlie Parker
正是我所需要的。谢谢! - Dinesh Shekhawat
1
还对 --include "*/" 感到好奇 - rsync 手册中的过滤规则说,“第一个匹配的模式会被执行”,所以我不明白为什么如果我们只使用第二个包含模式 (--include="*.sh"),这个命令就不能正常工作 - 难道这不是第一个匹配并执行我们想要的文件的模式吗? - David Streid
rsync是设计成在落入敌手之手时无法使用的吗?! - undefined

46

这是 man 手册中的重要部分:

当构建文件/目录传输列表时,rsync 会逐个检查要传输的名称与包含/排除模式列表匹配情况,第一个匹配的模式将被执行:如果它是一个排除模式,则跳过该文件;如果它是一个包含模式,则不跳过该文件名;如果没有匹配的模式,则不跳过该文件名。

总结一下:

  • 没有匹配任何模式意味着文件将被复制!
  • 一旦找到任何匹配模式,算法就会停止

同时,以斜杠结尾的内容是匹配目录的(就像 find -type d 命令一样)。

让我们分析以上回答。

rsync -zarv  --prune-empty-dirs --include "*/"  --include="*.sh" --exclude="*" "$from" "$to"
  1. 不要跳过任何目录
  2. 不要跳过任何.sh文件
  3. 跳过所有内容
  4. (隐含地,不跳过任何内容,但是上面的规则会阻止默认规则的发生。)

最后,--prune-empty-directories 避免第一条规则在各个地方创建空目录。


1
非常感谢您解释发生了什么。现在我不会忘记命令的机率更大了。 - MohamedEzz
11
“一旦任何模式匹配成功,算法就会停止”——这是关键,没有任何排名较高的答案像你在这里那样清楚地并且直截了当地解释了它。当然,这确实在手册中某个地方有提到,如果我仔细阅读了整篇手册,我就应该能看到。还是感谢你。 - TheDudeAbides
2
另一个关键概念是,“当使用--recursive(-r)选项(这是-a隐含的),每个路径的每个子目录组件从左到右访问,每个目录在其内容之前有机会被排除。通过这种方式,包括/排除模式递归地应用于每个节点的路径名”。 - wisbucky
算法一旦匹配任何模式就会退出 -- 如果是这样的话,--include "*/" 不应该允许同步任何目录中的任何文件吗?或者匹配文件是否需要同时匹配目录模式和文件模式? - FlexMcMurphy
1
@FlexMcMurphy - "*匹配任何路径组件,但会在斜杠处停止。" - Jim Hunziker
显示剩余2条评论

20

另外要补充的一点是:如果你需要在仅一个目录中按其扩展名同步文件(不进行递归),则应该使用以下结构:

rsync -auzv --include './' --include '*.ext' --exclude '*' /source/dir/ /destination/dir/

请注意第一个--include中的点号。在这种情况下,--no-r无效。

编辑:

感谢gbyte.co的宝贵评论!

编辑:

-uzv标志与此问题无直接关系,但我通常会使用它们。


1
你是怎么知道标志的顺序和它们需要包含什么的? - Charlie Parker
1
@CharlieParker,因为rsync使用includeexclude选项的顺序是按照它们在命令中出现的顺序。此外,它会在第一个匹配到的选项处停止。因此,在这个例子中,如果我们在第一位指定了--exclude '*',rsync将不会执行任何操作。请参阅手册以获取更多解释。 - Serge Roussak
1
如果我有递归目录,并且只想发送一种类型的文件,该如何编写命令?似乎它只对目标目录执行此操作。 - Charlie Parker
1
谢谢!需要使用 --include '*.ext' 而不是 --include '.ext' - gbyte
如果标志“-u”,“-z”或“-v”对答案没有意义,请考虑删除它们。 - CervEd
显示剩余2条评论

3

我编写了这个方便的函数,并将其放在我的bash脚本或~/.bash_aliases中。在安装有bash和awk的Linux本地测试同步,它能够正常工作。

selrsync(){
# selective rsync to sync only certain filetypes;
# based on: https://dev59.com/Dmgu5IYBdhLWcg3wv5ca#11111793
# Example: selrsync 'tsv,csv' ./source ./target --dry-run
types="$1"; shift; #accepts comma separated list of types. Must be the first argument.
includes=$(echo $types| awk  -F',' \
    'BEGIN{OFS=" ";}
    {
    for (i = 1; i <= NF; i++ ) { if (length($i) > 0) $i="--include=*."$i; } print
    }')
restargs="$@"

echo Command: rsync -avz --prune-empty-dirs --include="*/" $includes --exclude="*" "$restargs"
eval rsync -avz --prune-empty-dirs --include="*/" "$includes" --exclude="*" $restargs
}

优点:

在需要添加更多参数时(如--dry-run),它既简短又方便,并且易于扩展。

示例:

selrsync 'tsv,csv' ./source ./target --dry-run

-1
如果有人在寻找这个... 我想只同步特定的文件和文件夹,并成功通过这个命令实现:rsync --include-from=rsync-files 使用 rsync-files:
my-dir/
my-file.txt

- /*

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