2

Is there a way to have cp's --update option print files that did not copy because the source directory contained a newer version of the file?

3
  • 4
    There is no 'bash cp command'. There is a Unix/Linux cp command, and you might invoke it from bash .. or zsh .. or ksh .. or sh ..... Commented Apr 15, 2012 at 2:06
  • I don't think so, but the -v option will tell you when files DID copy. Perhaps you can figure out the ones that didn't from that? Commented Apr 15, 2012 at 2:08
  • 4
    @bmargulies Although you're correct, that's kind of a pedantic distinction. He obviously meant invoking the cp command from the bash shell. Commented Apr 15, 2012 at 2:11

3 Answers 3

5

Replace cp -ur with rsync -urvv.

The two "v"-s there are not a typo: they are there for increased verbosity, to show the files that were skipped.

The skipped files will look like the_filename is newer, so to get the list of skipped files you can use this one-liner:

rsync -urvv source/ target | grep ' is newer$' | sed -e 's/ is newer$//'

Or if you will be using it interactively the --progress flag can be very useful.

1
  • 1
    This is much better than my posted solution. +1 for remembering rsync. Commented Apr 22, 2012 at 14:24
1

I don't think there is a way to have cp do that directly, but this should do essentially the same thing:

cp -vu file destination | awk '{ gsub("[`\x27]", "", $1); print $1 } END { if (NR == 0) print "NOFILES" }' | xargs -I{} find . ! -name {} -maxdepth 1

This little one-liner comes with some limitations:

1) It depends on there not being any files named "NOFILES". You can change that string in the awk command to suite your needs.

2) You must be in the directory where file exists. If you are not in that directory, you need to change the find command to be find dirWhereFileExists instead of find . If you aren't copying from a single directory you can use find dir1 dir2... instead.

3) It won't work if you for some reason have backticks or single quotes in your filenames.

As you can tell this isn't the most robust solution, but it should do for a one-off operation.

EDIT

I woke up this morning and realized that the above solution was garbage. If you tried to copy multiple files it would search as many times as you have files, excluding one file each time. The following solution, however, should work:

cp -vu file destination | awk '{ gsub("[`\x27]", "", $1); regex = $1 "|" regex } END { if (NR == 0) { regex = "NOFILES|" } print ".*/("substr(regex, 0, length(regex))")" }' | xargs -I{} find . -regextype posix-extended ! -regex {} -maxdepth 1

It builds up a regex as it goes and passes that to find instead. Same limitations as above apply.

Sorry about the brain-fart earlier. Guess it was too late to be on StackOverflow.

0

Would rsync do the job for you? It has many options that fit your description, say "don't overwrite newer files" and such. And you can have it log findings. I use it often for remote AND local file copying.

You can get very granular with it too.

Good luck.

You must log in to answer this question.