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.

If I do

find . -exec echo {} +

it prints all paths in one line, i.e. command echo is only executed once.

But according to man find,

-exec command {} +
    ... the number of invocations of the command will 
be much  less  than  the  number  of matched files. ...

It seems that in some circumstances the command will be executed multiple times. Am I right? Please exemplify.

share|improve this question

2 Answers 2

up vote 7 down vote accepted

POSIX defined find -exec utility_name [argument ...] {} + as:

The end of the primary expression shall be punctuated by a <semicolon> or by a <plus-sign>. Only a <plus-sign> that immediately follows an argument containing only the two characters "{}" shall punctuate the end of the primary expression. Other uses of the <plus-sign> shall not be treated as special. If the primary expression is punctuated by a <semicolon>, the utility utility_name shall be invoked once for each pathname and the primary shall evaluate as true if the utility returns a zero value as exit status. A utility_name or argument containing only the two characters "{}" shall be replaced by the current pathname. If a utility_name or argument string contains the two characters "{}", but not just the two characters "{}", it is implementation-defined whether find replaces those two characters or uses the string without change.

If the primary expression is punctuated by a <plus-sign>, the primary shall always evaluate as true, and the pathnames for which the primary is evaluated shall be aggregated into sets. The utility utility_name shall be invoked once for each set of aggregated pathnames. Each invocation shall begin after the last pathname in the set is aggregated, and shall be completed before the find utility exits and before the first pathname in the next set (if any) is aggregated for this primary, but it is otherwise unspecified whether the invocation occurs before, during, or after the evaluations of other primaries. If any invocation returns a non-zero value as exit status, the find utility shall return a non-zero exit status. An argument containing only the two characters "{}" shall be replaced by the set of aggregated pathnames, with each pathname passed as a separate argument to the invoked utility in the same order that it was aggregated. The size of any set of two or more pathnames shall be limited such that execution of the utility does not cause the system's {ARG_MAX} limit to be exceeded. If more than one argument containing the two characters "{}" is present, the behavior is unspecified.

When length set of file name you found exceed system ARG_MAX, the command is executed.

You can get ARG_MAX using getconf:

$ getconf ARG_MAX
2097152

On some system, actual value of ARG_MAX can be different, you can refer here for more details.

share|improve this answer
    
I ran an experiment using find / -exec echo | wc and measuring the ratio between character count and line count I found that maximum command line length used by find is significantly smaller than theoretical POSIX limit, and a lot closer to the Size of command buffer we are actually using line in the output from xargs --show-limits. This is true for Linux and it may be true for Mac OS implementation of find, although xargs won't print the value in Mac OS. Any idea on why this happens? –  pqnet Aug 29 at 5:06
    
--show-limits is not specify by POSIX, Mac OS implementation of xargs does not support it. find / -exec echo | wc won't work. Remember that ARG_MAX return bytes. And it's maximum length of the arguments to the exec(3) functions. –  cuonglm Aug 29 at 5:55
    
I know --show-limits is not POSIX, though this is not the maximum argument length used by find, which uses a smaller value. I don't understand why you say that find / -exec echo | wc won't work: in my opinion it is a good way to have an estimate of the real value (and from what I can see, better than using getconf ARG_MAX). Also, my filesystem is mostly if not all ASCII character, so the number of characters is approximately the same as the number of bytes. –  pqnet Aug 29 at 6:55
    
@pqnet: use find / -exec sh -c 'echo $@ | wc -c' _ {} + isntead. –  cuonglm Aug 29 at 7:05
    
sorry I wrote it wrong, I actually used find / -exec echo {} + | wc -lc –  pqnet Aug 29 at 7:09

There is a maximum length of argument list for a new process in POSIX system. find will split the execution if the files paths are longer than this. To see the limit on Linux, use xargs --show-limits (don't work in Mac OS, if someone knows a better alternative please comment here)

edit: stolen straight from Gnouc's answer, the POSIX way to get the maximum length of argument list is getconf ARG_MAX. However, I ran an experiment on my mac os machine, and it looks like find uses a little more than half that number. This is coherent with the fact that, on system where it works, xargs --show-limits tells us that it won't be using the maximum argument length (in this case too it will use about a half that number), however I couldn't find an explanation for that.

edit 2: it seems that the only reliable way to determine how many parameters find will stick together for each invocation is to experiment, for example by running

find / -exec echo {} + | wc -cl

As the output from find has a line for each echo invocation, it is possible to count them using wc -l. The total number of bytes echoed is the output of wc -c instead. Dividing one by the other you get the average number of bytes in the parameters for each command invocation (albeit a slightly lower value, because of rounding, roughly half the average lenght of a path in your system)

share|improve this answer
    
xargs does not use the full maximum argument length because many programs prepend a few additional arguments and then pass the arguments to other programs. If xargs fills the arguments to the absolute maximum, such programs break, because there would be no room for those extra arguments. –  hvd Aug 29 at 12:36
    
@hvd makes sense. But then, is there a POSIX way to know how much of the buffer is used by xargs or find? –  pqnet Aug 29 at 19:30
    
You can execute it with a very long list of arguments, determining how many arguments were passed in the first invocation (something like yes . | xargs | head -n 1 | wc -c) and comparing that to the output of getconf ARG_MAX. But, actually trying it on my system, I get a difference is so large that it seems like there is more to this than I am aware of. –  hvd Aug 29 at 19:36
    
so it boils down to experimenting... I'll update my answer –  pqnet Aug 29 at 20:17

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.