Best way to sync files – copy only EXISTING files and only if NEWER than target

backupfile-copyrsyncsynchronization

I'm doing this sync locally on Ubuntu 12.04. The files are generally small text files (code).

I want to copy (preserving mtime stamp) from source directory to target but I only want to copy if the file in target already exists and is older than the one in source.

So I am only copying files that are newer in source, but they must exist in target or they won't be copied. (source will have many more files than target.)

I will actually be copying from source to multiple target directories. I mention this in case it impacts the choice of solution. However, I can easily run my command multiple times, specifying the new target each time, if that's what is required.

Best Answer

I believe you can use rsync to do this. The key observation would be in needing to use the --existing and --update switches.

        --existing              skip creating new files on receiver
        -u, --update            skip files that are newer on the receiver

A command like this would do it:

$ rsync -avz --update --existing src/ dst

Example

Say we have the following sample data.

$ mkdir -p src/; touch src/file{1..3}
$ mkdir -p dst/; touch dst/file{2..3}
$ touch -d 20120101 src/file2

Which looks as follows:

$ ls -l src/ dst/
dst/:
total 0
-rw-rw-r--. 1 saml saml 0 Feb 27 01:00 file2
-rw-rw-r--. 1 saml saml 0 Feb 27 01:00 file3

src/:
total 0
-rw-rw-r--. 1 saml saml 0 Feb 27 01:00 file1
-rw-rw-r--. 1 saml saml 0 Jan  1  2012 file2
-rw-rw-r--. 1 saml saml 0 Feb 27 01:00 file3

Now if I were to sync these directories nothing would happen:

$ rsync -avz --update --existing src/ dst
sending incremental file list

sent 12 bytes  received 31 bytes  406.00 bytes/sec
total size is 0  speedup is 0.00

If we touch a source file so that it's newer:

$ touch src/file3 
$ ls -l src/file3
-rw-rw-r--. 1 saml saml 0 Feb 27 01:04 src/file3

Another run of the rsync command:

$ rsync -avz --update --existing src/ dst
sending incremental file list
file3

sent 115 bytes  received 31 bytes  292.00 bytes/sec
total size is 0  speedup is 0.00

We can see that file3, since it's newer, and that it exists in dst/, it gets sent.

Testing

To make sure things work before you cut the command loose, I'd suggest using another of rsync's switches, --dry-run. Let's add another -v too so rsync's output is more verbose.

$ rsync -avvz --dry-run --update --existing src/ dst 
sending incremental file list
delta-transmission disabled for local transfer or --whole-file
file1
file2 is uptodate
file3 is newer
total: matches=0  hash_hits=0  false_alarms=0 data=0

sent 88 bytes  received 21 bytes  218.00 bytes/sec
total size is 0  speedup is 0.00 (DRY RUN)
Related Question