递归复制具有给定名称的目录

递归复制具有给定名称的目录

使用构建的目录结构

#!/bin/bash
for name in A B
do
    mkdir -p /tmp/src/${name}/copyme
    echo "do not copy" > /tmp/src/${name}/no.txt
    echo "copy" > /tmp/src/${name}/copyme/yes.txt
done

我只想将copyme目录及其中的文件镜像到 /tmp/tgt。

这应该很简单。依靠rsync区分命令行选项的顺序:排除所有内容,然后包含相关模式。然而

rsync -av --exclude='*' --include='copyme' /tmp/src/ /tmp/tgt/

排除所有内容(仅创建目标目录)。为什么?

答案1

运行时rsync,它会根据模式测试在源处找到的名称,并且第一个匹配的模式生效:

$ rsync -avv --exclude='*' --include='copyme' /tmp/src/ /tmp/tgt/
sending incremental file list
[sender] hiding directory A because of pattern *
[sender] hiding directory B because of pattern *
delta-transmission disabled for local transfer or --whole-file
total: matches=0  hash_hits=0  false_alarms=0 data=0

sent 51 bytes  received 86 bytes  274.00 bytes/sec
total size is 0  speedup is 0.00

当目录已被排除时(请参阅上面的“隐藏目录...”),将不再考虑其内容。反转排除和包含模式不会有帮助,因为它永远不会到达copyme目录。

手册rsync说:

例如,要包含/foo/bar/baz,目录/foo/foo/bar不得被排除。排除这些父目录之一会阻止对其内容的检查,切断 rsync对这些路径的递归并使包含 /foo/bar/baz无效(因为rsync无法匹配在目录层次结构的截止部分中从未看到的内容)。

所以与其:

$ rsync -avv --include='[AB]' --include='copyme/***' --exclude='*' /tmp/src/ /tmp/tgt/
sending incremental file list
[sender] showing directory A because of pattern [AB]
[sender] showing directory B because of pattern [AB]
[sender] showing directory A/copyme because of pattern copyme/***
[sender] hiding file A/no.txt because of pattern *
[sender] showing file A/copyme/yes.txt because of pattern copyme/***
[sender] showing directory B/copyme because of pattern copyme/***
[sender] hiding file B/no.txt because of pattern *
[sender] showing file B/copyme/yes.txt because of pattern copyme/***
created directory /tmp/tgt
delta-transmission disabled for local transfer or --whole-file
./
A/
A/copyme/
A/copyme/yes.txt
B/
B/copyme/
B/copyme/yes.txt
total: matches=0  hash_hits=0  false_alarms=0 data=10

sent 305 bytes  received 175 bytes  960.00 bytes/sec
total size is 10  speedup is 0.02

请注意,排除项必须位于包含项之后。该copyme/***模式匹配copyme目录名本身及其下面的任何路径名。

如果您不想对AB目录名称进行硬编码:

for dir in /tmp/src/*; do
    [ ! -d "$dir" ] && continue
    rsync -avv --include="${dir##*/}" --include='copyme/***' --exclude='*' /tmp/src/ /tmp/tgt/
done

这将输出

sending incremental file list
[sender] showing directory A because of pattern A
[sender] hiding directory B because of pattern *
[sender] showing directory A/copyme because of pattern copyme/***
[sender] hiding file A/no.txt because of pattern *
[sender] showing file A/copyme/yes.txt because of pattern copyme/***
created directory /tmp/tgt
delta-transmission disabled for local transfer or --whole-file
./
A/
A/copyme/
A/copyme/yes.txt
total: matches=0  hash_hits=0  false_alarms=0 data=5

sent 180 bytes  received 148 bytes  656.00 bytes/sec
total size is 5  speedup is 0.02
sending incremental file list
[sender] hiding directory A because of pattern *
[sender] showing directory B because of pattern B
[sender] showing directory B/copyme because of pattern copyme/***
[sender] hiding file B/no.txt because of pattern *
[sender] showing file B/copyme/yes.txt because of pattern copyme/***
delta-transmission disabled for local transfer or --whole-file
B/
B/copyme/
B/copyme/yes.txt
total: matches=0  hash_hits=0  false_alarms=0 data=5

sent 180 bytes  received 117 bytes  594.00 bytes/sec
total size is 5  speedup is 0.02

结果将是

$ tree src tgt
src
|-- A
|   |-- copyme
|   |   `-- yes.txt
|   `-- no.txt
`-- B
    |-- copyme
    |   `-- yes.txt
    `-- no.txt

4 directories, 4 files
tgt
|-- A
|   `-- copyme
|       `-- yes.txt
`-- B
    `-- copyme
        `-- yes.txt

4 directories, 2 files

另一种方法不使用排除或包含模式,rsync但用于find定位copyme目录然后rsync复制它们:

find /tmp/src -type d -name 'copyme' -prune -exec sh -c '
    cd /tmp/src && rsync -aRvv "${1#/tmp/src/}/" /tmp/tgt/' sh {} ';'

请注意此处使用的-R( --relative) 标志rsync

sh -c对每个找到的copyme目录执行的脚本都会执行tocd操作/tmp/src,然后复制路径名并/tmp/src删除其路径的初始位。

-prune命令中的停止find在已找到的目录中find查找其他目录。copyme

相关内容