28

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.

Jeff Schaller
  • 66,199
  • 35
  • 114
  • 250
MountainX
  • 17,168
  • 59
  • 155
  • 264

1 Answers1

42

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)
Jeff Schaller
  • 66,199
  • 35
  • 114
  • 250
slm
  • 363,520
  • 117
  • 767
  • 871
  • Thank you. Don't I need a few more rsync options? I'm reading the man page. Seems like I might need this: `rsync --archive --update --existing --whole-file --itemize-changes a/ b`. Or are most of those options unnecessary? (I added whole file because these are mostly small text files.) – MountainX Feb 27 '14 at 06:14
  • @MountainX - only add options you need. I'd start with `-a` that's a superset of switches, `-rlptgoD`. – slm Feb 27 '14 at 06:17
  • Let me clarify that. I'm confused about `--update` and `--existing`. Do I need both? I tested your solution and it seems to work, but I still feel safer adding `--update` . – MountainX Feb 27 '14 at 06:20
  • @MountainX - sure you can add it in, I'll put it in the A too. – slm Feb 27 '14 at 06:24