I want to execute a command in a list of directories : gradle --build-cache. Additionnly the directory need to start with the string 'Project" and not ending with "Test". I succeed doing that with this command:

find . -maxdepth 1 -type d \( ! -name . \) -name "Project*" ! -name "*Test*" -exec sh -c "cd '{}' && pwd && gradle --build-cache" \;

But my problem is that the gradle command can fail. If I launch the command alone it returns 'exit 1' but if one of the directory command failed it do not return the fail and the script continue. At the end, the loop return success exit 0...

How can I stop the loop execution when the gradle command failed and return for the general command an exit 1 ?

Thanks :)

  • 3
    When you say "loop", you mean the find process, right? You want find to abort when any of the commands started by -exec fails? Or just for find to return an error in that case? – ilkkachu Dec 1 '17 at 14:58
  • Yeah, the loop I talk about, it's the find. I want that if gradle --build-cache return failed, I want tje whole command (the find one) returns failed – Fred Dec 4 '17 at 9:00

This is a similar question to https://apple.stackexchange.com/questions/49042/how-do-i-make-find-fail-if-exec-fails. I'm citing the two most likely useful ways of how to achieve your goal.

The easiest way is to use xargs, however this works only if you have no space in any path the find command can find:

find … -print0 | xargs -0 -n1 invalid_command

You can also get rid of find and use recursive globbing, or implement a recursive search on your own (I doubt your directory tree is deep enough to cause an issue) if you are stuck with an older Bash (bellow 4.x.x).

shopt -s globstar
set -e
for x in **/*.xml; do invalid_command "$x"; done

Also, if you don't need it to be a one-liner, you can at first find all the directories, save them e.g. to a tempfile (use mktemp for that), and then do anything you need with the list. Filter it further, run commands on them...

  • Collect 50 rep and convert it to a comment! Now I am too drunken to do it for you! But I love you! – peterh Dec 1 '17 at 18:27
  • Edited so it is not a link-only answer. – Zopper Dec 1 '17 at 20:12
  • Thanks ! I don't have any space in my directories names. I will try the xargs. It's true that I trying to make a one-line command but maybe it's more easy to make multiple ones... – Fred Dec 4 '17 at 9:03
  • find -print0 | xargs -0 should deal with whitespace in names just fine, provided of course that whatever xargs runs handles them properly. Also, it's probably worth noting, that the xargs solution will process all files, even if one of them fails. (xargs returns 123 if a command returns 1 to 125.) On the other hand, the loop here will abort immediately on a failure (because of the set -e), you'd need to make some flag variable to remember the error but to continue anyway. – ilkkachu Dec 4 '17 at 9:35

You can test to see if the last command exited with an error and then issue an exit command by appending if [[ $? != 0 ]] ; then exit ; fi to the line.

Please note though that if the provided code snippit is part of a bigger script this will exit the script completely. You may want to consider rewriting it into a for or while loop so that you can use break instead.

  • Well exit doesn't really do anything with find, it doesn't even mind what the exit code is. – ilkkachu Dec 4 '17 at 9:36

Just for fun, here's a find-only answer. I don't think you can do this with only the POSIX features of find, but since you're already using some GNUisms, you can take advantage of the -quit primitive:

$ find -exec command {} \; -o -quit

This makes find quit immediately as soon as command fails (i.e. returns a non-zero exit code). Now, to force find to also exit with a non-zero status, you need to get it to do something that will fail before the -quit. One way to do this is

$ find -exec command {} \; -o -fprint /dev/full -quit

since all writes to /dev/full will fail. You will get an error message like

find: ‘/dev/full’: No space left on device

which you can filter out with 2>/dev/null if you want.

If you were using NetBSD find, you could do this instead:

$ find -exec command {} \; -o -exit 1

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.