Is there a way to write an find so that it breaks if one of the -exec operations fails on a file?

E.g. (javac is conveniently used as a program that can return an exit code of 1 on some files and for no other reason):

$ echo "public classXX A{}" >> A.java
$ echo "public class B{}" >> B.java
$ find . -iname \*.java -exec javac {} \;
./A.java:1: error: class, interface, or enum expected
public classXX A{}
1 error

In the example above, although the exec on the file A.java failed (and returned an exit code of 1), the find command proceeded and compiled file B.java as well. Is there a way to break out of find or should I use some form of for instead?

My version of find:

$ find -version  | head -1
find (GNU findutils) 4.4.2

I think...

find ... -exec sh -c 'cmd "$0" || kill $PPID' \{\} \;

...should do the trick for just about any find.

  • Thanks @Gilles. What does the $0 do? – mikeserv Jun 6 '14 at 0:23
  • 1
    $0 is the first argument to sh -c ($1 is the second one, etc.) — conventionally the name of the program being executed, but here you're executing a snippet, and passing the file name that's being processed as $0 makes some sense (e.g. you easily see it in ps). There's a school of thought (which I obviously don't belong to) that states that $0 should always be executable and you should write sh -c 'cmd "$1" …' sh {} or some such. – Gilles 'SO- stop being evil' Jun 6 '14 at 0:27
  • @Gilles - pretty slick. Thanks a lot. I figured it out in the car on the way to the preschool, but I had to think about it for several minutes. That's smart - I'm gonna use it. What actually clicked it over for me was an edit I remembered reading of Stephane's on one of your own posts in which he added a find-sh $0 to the end of a find statement. This seems more useful in my opinion, though I guess it's what you're after in the long run that matters most. – mikeserv Jun 6 '14 at 0:46
  • It's not working for the case above. After find . -iname \*.java -exec sh -c 'javac "${0}" || kill '$$ {} \; file A.java fails and, still, file B.java gets compiled. – Marcus Junius Brutus Jun 6 '14 at 19:42
  • @MarcusJuniusBrutus - I fixed it. I should've seen your comment earlier. Again, sorry. – mikeserv Jun 8 '14 at 0:37

At least with GNU find you can use:

find ... -exec ... -o -quit
  • I seem to have trouble making it clear where the exec stops and the -o starts. Both find . -iname \*.java -exec javac {} -o -quit \; and find . -iname \*.java -exec javac {} \; -o -quit \; fail (in different ways) – Marcus Junius Brutus Jun 5 '14 at 17:36
  • @MarcusJuniusBrutus \; is needed at the and of -exec (i.e. before -o -quit in this case) but may be nowhere else. – Hauke Laging Jun 5 '14 at 17:47
  • 2
    Using -exec ... -o -quit does not work for me. It does not process any files. However this line works: find . -iname \*.java -not -exec javac '{}' \; -quit: -quit is called if -exec is false, ie. returns with non-zero. – savanto Jun 5 '14 at 18:31
  • find . -iname \*.java -exec javac {} \; -o -quit, too, fails to process any files. Strangely, find . -iname \* -exec echo 'foo' \; -o -quit does "process" the files. – Marcus Junius Brutus Jun 5 '14 at 19:01

Consult your find manual. @Hauke's answer motivated me to look and QNX's find has an -abort option that seems similar to the GNU -quit option. It works for me provided I use \( and \) to group expressions:

find . -mtime -4 -name <pattern> \( -exec my_cmd {} \; -o  -abort \) 

Note: without the parens, it aborts on the first file.

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.