Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

@StephaneChazelas posted the following solution to this Q&A: Having some trouble using “find -exec {} +”.

$ find . -iname "*.extension" -exec sh -c '
  exec <command> "$@" <additional parameters>' sh {} +

What exactly is going on here? Specifically what does the last sh {} do? It seems as though it's just there to pacify find's -exec command so that it has something to do, a NOOP.

I could just as easily put echo {} there and it appears to work just fine.

share|improve this question

2 Answers 2

up vote 6 down vote accepted

The syntax is:

find ... -exec cmd {} +

find will find a number of files based on the criteria in ... and run cmd with that list of file paths as arguments, as many as possible while avoiding to reach the limit on the size of the arguments to a command.

If needed it may split the list of files and call cmd several times. For instance, it may end up calling:

cmd ./file1 ./file2 ... ./file3000
cmd ./file3001 ./file3002 ... ./file4321

A limitation with that is that {} has to be last. You can't write for instance:

find ... -exec cmd {} other args +

like you would with ';' instead of '+'.

You can write:

find ... -exec echo foo {} +

but not:

find ... -exec echo {} foo +

So, if you do need to add some extra arguments to cmd after the list of files, that's where you have to resort to calling a shell (other reasons why you'd need to call a shell would be any time you need to use a shell feature like redirections, pipes, some string expansions...).

In sh -c 'inline-script' x a b c, for the inline-script, $0 is x, $1 is a, $2 is b... so "$@" is the list of those 3 arguments: a, b and c. So in:

find ... -exec sh -c 'cmd "$@" other arg' find-sh {} +

For the inline script, $0 (which is used for instance when displaying error messages) is set to find-sh and "$@" is the list of files (what find expands {} to).

By using the exec special builtin of the shell:

find ... -exec sh -c 'exec cmd "$@" other arg' find-sh {} +

We tell the shell not to fork an extra process to run cmd, but instead to run it in the same process. Some shells like bash, zsh and some implementations of ksh do that implicitly for the last command in a script.

share|improve this answer
    
Could you use a subshell instead of exec there? -exec sh -c '(cmd1; cmd2;)' find-sh {} +? –  slm Oct 2 '13 at 19:13
    
So if I understand you correctly, the find-sh {} are arguments to the sh -c '...' command, right? –  slm Oct 2 '13 at 19:22
    
@slm, find will call /bin/sh with as arguments ("sh", "-c", "...", "find-sh", "./file1", "./file2"...). And inside ..., that maps (the shells maps that) to $0` being "find-sh", and the positional parameters ($1, $2... which you could say are the arguments to the inline script) being ./file1, ./file2. –  Stéphane Chazelas Oct 2 '13 at 19:45

sh {} It means Running command in shell and whole entry exactly means match entry by find command...

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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