0

I'm trying to write a script to copy a set of directories that were created today to another directory.

I'm currently using:

find /test/downloads/ -mindepth 1 -maxdepth 1 -type d -mtime -1 -printf '%f\n' | xargs -I '{}' cp -R '{}' /test/uploads/

But that gives me an error of:

cp: cannot stat 'foo': No such file or directory

What am I missing?

2 Answers2

4

-printf '%f\n' is a GNU extension that prints the trailing portion of the current file path followed by a newline character. If the file path is /test/downloads/foo, it prints foo\n.

xargs takes that output. Here foo contains none of the characters treated specially by xargs with -I{} (quoting characters, newlines (the only delimiter with -I) and leading whitespace, an EOF character with some).

So {} is replaced with foo in the arguments passed to cp. So cp ends up being called with cp, -R, foo, /test/uploads/ as arguments.

So cp will look for foo in the current working directory, not in /test/downloads.

If the file had been called /test/downloads/-t.., the command would have been

cp -R -t.. /test/uploads/

Which tells cp to copy /test/uploads/ to the parent directory of the current working directory.

If the file name had been /test/downloads/ 'blah'<newline>"blah", cp would have been called twice as cp -R blah /test/uploads/.

So you have two problems:

  1. With %f, you're stripping the leading directory components to the file
  2. You're using xargs which cannot be used reliably on the output of find (other than with the non-standard -r and -0 options to process NUL-delimited records).

One could also add that you're calling cp for each directory even though cp is well capable of copying more than one file at a time.

Here, standardly, that should be:

find /test/downloads/. ! -name . -prune -type d -mtime -1 -exec sh -c '
  exec cp -R "$@" /test/uploads/' sh {} +

Which with GNU implementations of those utilities (as found by default in your Debian system) can be simplified to:

find /test/downloads/ -mindepth 1 -maxdepth 1 -type d -mtime -1 \
  -exec cp -Rt /test/uploads/ {} +
0

You probably have a file named "foo bar".

A better way would be to replace from your -printf ... to the end with:

-print0 | xargs -0 -r cp -t /Test/Uploads

Read man find xargs cp.

waltinator
  • 4,865