我想使用 rsync 同步两个目录,并保留所有文件的权限。我的用户foo
对目标目录具有写权限,但不拥有该目录。
$ ls -l
total 8
drwxrwx--- 2 foo foo 4096 Jun 3 16:01 a
drwxrwxr-x 3 root foo 4096 Jun 3 16:02 b
虽然同步确实有效,但它会导致权限错误和错误的退出代码:
$ rsync -av -O --delete a/ b/
sending incremental file list
rsync: failed to set permissions on "/tmp/r/b/.": Operation not permitted (1)
deleting 2
./
1
sent 115 bytes received 138 bytes 506.00 bytes/sec
total size is 0 speedup is 0.00
rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1196) [sender=3.1.2]
我知道,我可以使用该--no-perm
选项,但这会阻止设置任何文件的权限,而不仅仅是目标目录的权限。
另一个解决方案是使用rsync -av --delete a/* b/
,但这会阻止从中a/
删除已删除的文件b/
。
这可能是这个问题,自 2010 年以来一直未得到解答 :-(
答案1
另一个解决方案是使用
rsync -av --delete a/* b/
,但这会阻止从中a/
删除已删除的文件b/
。
和然后你跑
rsync -rv --delete --existing --ignore-existing a/ b/
来处理这个问题。您在这里不使用,-a
因此目标目录的所有权不是问题。
从man 1 rsync
:
--existing
,--ignore-non-existing
这表示rsync
跳过创建目标上尚不存在的文件(包括目录)。如果此选项与 结合使用--ignore-existing option
,则不会更新任何文件(如果您只想删除无关文件,则此选项非常有用)。
(致谢这个答案。
改进:
一般来说,
*
匹配所有文件和目录是不够的。.[!.]*
如果您有点文件,并且..?*
文件名以两个点开头,您还需要。在 Bash 中dotglob
有帮助,因此不需要额外的模式:# in Bash shopt -s dotglob rsync -av --delete a/* b/
但您需要确保模式扩展为某个值。如果没有,
rsync
将获取其文字形式并发出抱怨。创建一个虚拟(临时)文件a/
可能不太雅观,但它肯定会使模式扩展。如果您在运行第二个文件之前删除该文件,rsync
那么它也将从目标中删除。- 如果你决定在
a/
目录中工作,模式将变为*
。在这种情况下,你应该使用双划线 (--
)或者修改模式(./*
),这样文件名-n
就不会被视为选项。
示例解决方案可能是这样的:
#!/bin/bash
shopt -s dotglob
tmpf="$(mktemp -p a/)" || exit 202
rsync -av --delete a/* b/
rm "$tmpf"
rsync -rv --delete --existing --ignore-existing a/ b/
笔记:
- 每个
rsync
都会生成自己的退出状态,您需要额外的逻辑来判断一切是否正常。 如果有很多对象,
a/
那么第一次调用rsync
可能会产生argument list too long
。 可能的解决方案:find
+xargs
。循环中逐个处理对象:
for f in a/*; do rsync -av --delete -- "$f" b/ done
(在这种情况下
shopt -s nullglob
提前使用,您甚至不需要虚拟文件)。注意我使用了
--
尽管在这种特殊情况下它不是必需的(因为扩展$f
必须以 开头a/
)。但是假设你修改代码并更改a/*
为*
。那么你需要--
添加另一行,很容易忽略这一点。