I want to sync two directories with rsync, preserving the permissions of all files. My user foo
does have write access to the target directory, but does not own it.
$ 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
While syncing does work, it causes a permission error and a bad exit code:
$ 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]
I am aware, that I could use the --no-perm
option, but this would prevent setting the permissions of any files and not just of the target directory.
Another solution would be to use rsync -av --delete a/* b/
, but this would prevent deleted files in a/
from being removed in b/
.
This is probably a duplicate of this question, which remains unanswered since 2010 🙁
Best Answer
And then you run
to deal with this. You don't use
-a
here so the ownership of the target directory is not a problem.From
man 1 rsync
:(Credits to this answer.)
Improvements:
In general
*
is not enough to match all files and directories. You also need.[!.]*
if you have dot files, and..?*
if you have file names beginning with two dots. In Bashdotglob
helps, so additional patterns are not needed:But you need to make sure that the pattern expands to something. If it doesn't,
rsync
will get its literal form and complain. Creating a dummy (temporary) file ina/
may be inelegant, but it will certainly make the pattern expand. If you remove the file before you run the secondrsync
then it will be removed from the destination as well.a/
directory, the pattern will become*
. In this case you should use double dash (--
) or modify the pattern (./*
) so file names like-n
are not treated as options.An example solution may be like this:
Notes:
rsync
generates its own exit status, you need additional logic to tell if everything was OK.If there are many objects in
a/
then the first invocation ofrsync
may yieldargument list too long
. Possible solutions:find
+xargs
.Processing objects one by one in a loop:
(in this case use
shopt -s nullglob
beforehand and you won't even need a dummy file).Note I used
--
even though it's not necessary in this particular case (because expanded$f
must start witha/
). But let's say you modify the code and changea/*
to*
. Then you need to add--
in another line, it's very easy to miss this.