POSIX Find Command – Why ‘find -exec cmd {} +’ Needs to End in ‘{} +’

findhistoryposix

Preface: I understand the difference between -exec {} \; & -exec {} +. I also don't have a problem as such, I am just curious about the semantics of find.


When ending the -exec argument with + instead of ;, we need to end this with {} +, for example:

# FreeBSD find
$ find . -type f -exec cp {} /tmp +
find: -exec: no terminating ";" or "+"

# GNU find is even more cryptic:
$ find: missing argument to `-exec'

Using ; in these examples instead of + works fine (but obviously does something else).

From POSIX:

-exec utility_name [argument ...] ;
-exec utility_name [argument ...] {} +

…   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.

In other words, when using the +, the command needs to end with {} +.

Why is this? And why only with the + and not the ;? At first I thought maybe to avoid conflicts with filenames that contain a +, but filenames with a ; seem to work fine? I find it hard to believe this limitation is arbitrary …

Best Answer

The rationale given in the POSIX specification is:

The "-exec ... {} +" syntax adopted was a result of IEEE PASC Interpretation 1003.2 #210. It should be noted that this is an incompatible change to the ISO/IEC 9899:1999 standard. For example, the following command prints all files with a '-' after their name if they are regular files, and a '+' otherwise:

find / -type f -exec echo {} - ';' -o -exec echo {} + ';'

The change invalidates usage like this. Even though the previous standard stated that this usage would work, in practice many did not support it and the standard developers felt it better to now state that this was not allowable.

PASC Interpretation 1003.2 #210 goes into more detail about the history of -exec … {} +. It existed on several Unix systems before it was adopted by POSIX; the defect report traces it back to SVR4 (where it was largely undocumented). The defect report justifies the incompatible change as having little impact in practice:

Note that the "+" is only treated as special if it immediately follows "{}". This minimises the chances of causing problems with existing uses of "+" as an argument with "-exec".

Although adding support for -exec … {} + would break some conforming applications such as the example above, there are fewer of these than if -exec … {} … + was allowed.

Another reason, perhaps, to restrict {} to be the last argument is ease of implementation. If {} was allowed anywhere in the argument list to -exec, the find program would have to build the command line by copying the static arguments, then the variable part, then another static part. This would make it harder to construct the argument list and to account for the size limit. The difficulty is minimal, but implementers like to cut corners. Supporting multiple substitutable instances of {} (if -exec {} foo + is to work, it can be logically expected that -exec {} foo {} + would as well) would be significantly harder.