17

I'm trying to run the following command:

find a/folder b/folder -name *.c -o -name *.h -exec grep -I foobar '{}' +

This is returning an error:

find: missing argument to -exec

I can't see what's wrong with this command, as it seems to match the man page:

-exec command {} +

This variant of the -exec option runs the specified command on the selected files, but the command line is built by appending each selected file name at the end; the total number of invoca- tions of the command will be much less than the number of matched files. The command line is built in much the same way that xargs builds its command lines. Only one instance of '{}' is allowed within the command. The command is executed in the starting directory.

I also tried:

find a/folder b/folder -name *.c -o -name *.h -exec grep -I foobar {} +
find a/folder b/folder -name *.c -o -name *.h -exec 'grep -I foobar' {} +
find a/folder b/folder -name *.c -o -name *.h -exec 'grep -I foobar' '{}' +
find a/folder b/folder -name "*.c" -o -name "*.h" -exec grep -I foobar '{}' +
find a/folder b/folder \( -name *.c -o -name *.h \) -exec grep -I foobar '{}' +
find a/folder b/folder -name *.c -o -name *.h -exec grep -I foobar '{}' \+
  • Have you tried escaping the + at the end? find a/folder b/folder -name *.c -o -name *.h -exec grep -I foobar '{}' \+ – jayhendren May 7 '14 at 21:37
  • 3
    You may be using an old version of GNU find. Though the -exec cmd {} + variant is POSIX and has been available since the 80s, GNU find only added it (relatively) recently (2005). What does find --version tell you? – Stéphane Chazelas May 7 '14 at 21:43
  • 2
    @Koveras, that would be it then. -exec {} + was added in 4.2.12 in 2005. In older GNU finds, you can use the (non-POSIX) -print0 | xargs -r0 to get something similar. 4.1 is from 1994. – Stéphane Chazelas May 7 '14 at 21:47
  • 1
    JRFerguson pointed out (in an answer that has been deleted) that the -name pattern arguments should be quoted: -name "*.c" -o -name "*.h".  This is true, although it’s unrelated to the -exec error.  You’ll notice that all the other answers put the wildcards into quotes, although only Gilles mentions it.  … (Cont’d) – G-Man Mar 18 at 20:32
  • 1
    (Cont’d) … jlliagre’s answer collapses the name expression to -name "*.[ch]" without explanation.  This has the benefits of simplifying the command line and, specifically, eliminating the -o.  Find expressions involving -o are hard to get right.  Yours is wrong; if your command is fixed so that it doesn’t error out (as in Gilles’s answer), it will run grep only on the .h files.  You need to do '(' -name '*.c' -o -name '*.h' ')'. – G-Man Mar 18 at 20:32
17

You need to remove the single quotes you use around {}. The command can be simplified like this:

find a/folder b/folder -name "*.[ch]" -exec grep -I foobar {} +

Should you use an archaic GNU find version, this should still work:

find a/folder b/folder -name "*.[ch]" -exec grep -I foobar {} \;
  • Oops, they were meant to be quotes not backticks. – David Kennedy May 7 '14 at 21:21
  • Quotes would be useless as {} has no specific meaning to the shell. – jlliagre May 7 '14 at 21:26
  • From find man pages: "The string '{}' is replaced by the current file name being processed everywhere it occurs in the arguments to the command, not just in arguments where it is alone, as in some versions of find. Both of these constructions might need to be escaped (with a '\') or quoted to protect them from expansion by the shell." – David Kennedy May 7 '14 at 21:29
  • 1
    I indeed read that in the manual page but the fact is there is no shell I'm aware of that requires quoting the curly braces. What shell are you using ? – jlliagre May 7 '14 at 21:37
  • bash. With or without the quotes I get the error anyways. – David Kennedy May 7 '14 at 21:38
8

“missing argument to -exec” usually means that the argument to -exec is missing its terminator. The terminator must either be an argument containing just the character ; (which needs to be quoted in a shell command, so it's typically written \; or ';'), or two successive arguments containing {} and +.

Stephane Chazelas has identified that you're using an older version of GNU find which doesn't support -exec … {} +, only -exec {} \;. Although GNU was a late adopter of -exec … {} +, I do recommend that you get a less antique tool suite (such as Cygwin, which includes git and a whole lot more, or GNUwin32, which lacks git but doesn't have the bad-employee-trying-to-use-linux-but-we-impose-windows vibe that Cygwin gives). This feature was added in version 4.2.12, over 9 years ago (it was the last identified feature to make GNU find POSIX-compliant).

If you want to stick to an older GNU find, you can use -print0 with xargs -0 to get a similar functionality: grouped command execution, supporting arbitrary file names.

find a/folder b/folder -name '*.c' -o -name '*.h' -print0 | xargs -0 grep -I foobar /dev/null

Always quote the wildcards on the find command line. Otherwise, if you happen to run this command from a directory containing .c files, the unquoted *.c would be expanded to the list of .c files in the current directory.

Adding /dev/null to the grep command line is a trick to ensure that grep will always print the file name, even if find happens to find a single match. With GNU find, another method is to pass the option -H.

  • 1
    What do you mean by bad-employee-trying-to-use-linux-but-we-impose-windows vibe that cygwin gives? – David Kennedy May 16 '14 at 20:36
  • GNUwin32 doesn't have expect :( – David Kennedy May 17 '14 at 16:18
  • See my comment(s) on the question. – G-Man Mar 18 at 20:33
2

If a command such as

find a/folder b/folder -name "*.c" -o -name "*.h" -exec grep -I foobar {} +

returns error

find: missing argument to -exec

the probable cause is too old GNU find which does not support syntax -exec mycommand {} +. In that case low performance replacement is to run -exec mycommand {} \; which will run the mycommand once for every found target instead of collecting multiple targets and running the mycommand just once.

However, GNU find does not support e.g.

find . -type f -and -name "*.ttf" -exec cp {} ~/.fonts +

because GNU find supports only literal combination {} + instead of more generic {} additional parameters +. Note that there cannot be anything between the braces and the + character. If you try this, you will get the same error:

find: missing argument to -exec

The workaround is to use syntax {} additional parameters \; which does work but will execute the command once for every found target. If you need more performance with GNU find you have to write a wrapper script that can append additional parameters to the arguments given. Something like

#!/bin/bash
exec mycommand "$@" additional parameters

should be good enough. Or, if you do not want to create temporary file, you can use one-liner to change order of parameters like this:

find . -type f -and -name "*.ttf" -exec bash -c 'mycommand "$@" extra arguments' {} +

which will execute mycommand {list of ttf files} extra arguments. Note that you may need to double escape special characters for the bash after the -c flag.

  • (1) The part of the above that actually answers the question was already given by other people. (2) What you are describing is not a flaw or deficiency in GNU find, but the correct behavior specified by POSIX. – G-Man Mar 18 at 20:03
  • 1
    +1 Finally, someone who answers why additional parameters don't work! It seems like a deficiency in the POSIX definition. – Jonathan Jul 11 at 18:29
  • If you've GNU find you've probably got GNU cp. In this case you could find ... -exec cp --target-directory ~/.fonts {} + to keep the {} at the end of the execution string. – roaima Aug 9 at 7:51
1

find . -type f -perm 0777 -exec chmod 644 {}\;

got error find: missing argument to ``-exec'.

Adding space between {} and \ fixed it:

find . -type f -perm 0777 -print -exec chmod 644 {} \;

  • 1
    There is no such issue in the find command in the question at hand. – Kusalananda Mar 18 at 18:59
  • In Question it is not , fine, that I understood , but problem is same "find: missing argument to ``-exec'" , Problem can be occur from different-2 reason, I answered because I saw same problem statement. – ShreePool Mar 20 at 7:38
  • @Kusalananda good grief, the noob provided a solution for the reported error which is stated by the OP in both the title and body of the question. – bvj Apr 27 at 0:41
  • @bvj The question explicitly deals with the + form of the -exec option to find. This answer is correcting a problem that the user asking the question does not have. – Kusalananda Apr 27 at 6:01
-1

I had my share of headaches with the exec syntax in the past. most days now I prefer the nicer bash syntax:

for f in `find a/folder b/folder -name "*.[ch]"`; do grep -I foobar $f; done

It has some limitations when you want to treat the files as a group, as each is evaluated serially, but you can pipe the output elsewhere just fine

  • 1
    While this tends to work it is significantly less useful than the pure-find version because it cannot handle files with whitespace in the name correctly. – Etan Reisner May 8 '14 at 3:35
  • 5
    No, don't do this. This breaks as soon as the files contain spaces and other “weird” characters. This is also more complex and slower than find … -exec … \;, so there's no reason to use this even if you know that your file names are tame. – Gilles May 8 '14 at 3:44
  • this was helpful for my situation where I needed to run multiple lines of logic based on the file names (like removing chars, making directories and then moving the files). Trying to get find to do multiple things in one exec was too much of a headache for the 5 minutes I wanted to spend on this. My file names were tame and this solved my problem :) – gMale May 24 at 21:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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