使用 rsync 设置除根目录之外的所有文件的权限

使用 rsync 设置除根目录之外的所有文件的权限

我想使用 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/**。那么你需要--添加另一行,很容易忽略这一点。

相关内容