Skip to content
Advertisement

I’ve found a way to use find -exec to cp multiple files all in one line like xargs, but I’m not sure exactly how it works

I’ve been working with find -exec and find | xargs for the past few hours exploring and experimenting, and now I’ve found a variation of the command that I haven’t seen anywhere else.

For example this find command to get all files in the child subdirectories and copy them to the current directory

find . -type f -regex './[^.].*/[^.*].*' -exec sh -c 'cp "$@" .' thiscanbeanything {} +

will all execute on one line like so:

cp ./testy/bar ./testy/baz ./testy/foo .

Instead of the usual:

find . -type f -regex './[^.].*/[^.*].*' -exec sh -c 'cp {} .' ;

which executes on multiple lines

cp ./testy/bar .
cp ./testy/baz .
cp ./testy/foo .

Moreover in the first command the output will be only:

cp ./testy/baz ./testy/foo .

Unless the sh -c 'cmd' is followed by something else, which in my example was thiscanbeanything.

Could someone elucidate what’s going on, or if this is even viable?

Advertisement

Answer

To understand what is going on, have a look at this:

$ sh -c 'echo 0=$0 1=$1 2=$2' thiscanbeanything one two
0=thiscanbeanything 1=one 2=two

This executes sh with the option -c 'echo 0=$0 1=$1 2=$2' and three arguments thiscanbeanything one two.

Normally, $0 is the name of the script being executed. When running sh -c, there is no script but the name is taken from the first argument that your provide which, in this case, is thiscanbeanything.

Documentation

This behavior is documented in man bash under the -c option:

-c string
If the -c option is present, then commands are read from string. If there are arguments after the string, they are assigned to the positional parameters, starting with $0.

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement