Reputation: 54581
I have read Is it possible to pass a find command to -exec of another find command? but I do not understand why.
I know the example command in this question is not very useful, this is just an example command.
Try to run the following command:
$ find -type d -exec find {} -ls + -exec echo {} +
find: Only one instance of {} is supported with -exec ... +
From my point of view the printed error is not correct because my command is composed of two -exec
each one having its own final +
find -type d -exec find {} -ls + -exec echo {} +
<-----------------> <------------->
First command Second command
When trying the first command only, I got a different error:
$ find -type d -exec find {} -ls +
find: missing argument to `-exec'
Try 'find --help' for more information.
Of course, a workaround is to replace +
by \;
. But I wonder why we got these error messages in the two above examples ??? Please, also provide elegant solutions :-)
For information, I am using find
on Ubuntu 16.10 and bash
:
$ find --version
find (GNU findutils) 4.7.0-git
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by Eric B. Decker, James Youngman, and Kevin Dalley.
Features enabled: D_TYPE O_NOFOLLOW(enabled) LEAF_OPTIMISATION FTS(FTS_CWDFD) CBO(level=2)
Upvotes: 1
Views: 191
Reputation: 438273
When -exec
is used with terminator +
(in contrast with \;
):
-exec
action only support a single instance of placeholder {}
and+
.Therefore, because the find
command you're passing to -exec ... +
invariably requires arguments after the {}
, it cannot work.
In other words: Using -exec ... +
, you cannot call any command that requires you to pass arguments after the list of filenames represented by {}
.
While the error messages may not be helpful, there is a good reason for these restrictions:
Due to (platform-dependent) limits on the length of a command line, -exec ... +
cannot guarantee that all filenames can be passed to a single invocation of the specified command; like with xargs
, the list may therefore have to be partitioned to perform multiple calls (as few as possible). Given this possibility: if placing arguments after {}
were allowed, the conceptual question would arise as to how to handle these arguments in the event multiple calls have to be made (pass them to every call? only the last one?), to which there is no clear answer.
These are my own inferences - do let me know if something is incorrect/should be added.
As you imply in your question, these restrictions do NOT apply to use of -exec ... \;
:
Using terminator \;
causes find
to invoke the command once per matching file, so that {}
expands to a single filename, in which case you're free to place {}
, possibly multiple times, anywhere in the command.
By contrast, -exec ... +
, causes find
to pass (typically) all matching filenames at once ({}
expands to a filename list) to a (typically) single overall invocation of the command.
Depending on the specific command, using \;
in lieu of +
may be a viable workaround, but it has important performance implications: with large input sets, the difference between creating an external process once per filename and a single process overall will be very noticeable.
As for the error messages:
$ find -type d -exec find {} -ls + -exec echo {} +
find: Only one instance of {} is supported with -exec ... +
What you meant to be two -exec
actions are parsed as one, because find
keeps parsing until it finds {}
immediately followed by +
. Per the POSIX specification for find
:Thanks, chepner.
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.
When interpreted as a single action, it now contains 2 instances of {}
, which isn't supported with +
, hence the error message (which, overall, is confusing).
$ find -type d -exec find {} -ls +
find: missing argument to `-exec'
This error stems from {}
not being the last argument before terminator +
, which means that the +
is not recognized as the terminator of the action, so the action as a whole is not syntactically valid.
Unfortunately, the error message confusingly suggests that the argument (the command to execute) is missing - calling it unterminated would make more sense.
Upvotes: 6