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?

  • Test your command by doing find ... | xargs -I xx echo xx /test/uploads/. In case xx is a relative path it may have to be adjusted or prefixed. – Andre Wildberg Jan 4 at 23:53
  • You probably need %p rather than %f – steeldriver Jan 4 at 23:54

-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/ {} +
  • Thank you so much, your explanation helped immensely! – Canadian Jan 5 at 18:56

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.