I'm wanting to copy files I've found with grep
on an OSX system, where the cp
command doesn't have a -t
option.
A previous posts' solution for doing something like this relied on the -t
flag in cp
. However, like that poster, I want to take the file list I receive from grep
and then execute a command over it, something like:
grep -lr "foo" --include=*.txt * 2>/dev/null | xargs cp -t /path/to/targetdir
Less efficient than cp -t
, but this works:
grep -lr "foo" --include=*.txt * 2>/dev/null |
xargs -I{} cp "{}" /path/to/targetdir
Explanation:
For filenames | xargs cp -t destination
, xargs
changes the incoming filenames into this format:
cp -t destination filename1 ... filenameN
i.e., it only runs cp
once (actually, once for every few thousand filenames -- xargs
breaks the command line up if it would be too long for the shell).
For filenames | xargs -I{} cp "{}" destination
, on the other hand, xargs
changes the incoming filenames into this format:
cp "filename1" destination
...
cp "filenameN" destination
i.e., it runs cp
once for each incoming filename, which is much slower. For a large number (e.g., >10k) of very small (e.g., <10k) files, I'd guess it could even be thousands of times slower. But it does work :)
PS: Another popular technique is use find
's exec
function instead of xargs
, e.g., https://stackoverflow.com/a/5241677/1563960