![递归复制具有给定名称的目录](https://linux22.com/image/135039/%E9%80%92%E5%BD%92%E5%A4%8D%E5%88%B6%E5%85%B7%E6%9C%89%E7%BB%99%E5%AE%9A%E5%90%8D%E7%A7%B0%E7%9A%84%E7%9B%AE%E5%BD%95.png)
使用构建的目录结构
#!/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
目录名本身及其下面的任何路径名。
如果您不想对A
和B
目录名称进行硬编码:
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