Search code examples
bashfind

How to make find terminate when an -execdir script fails?


I want to execute possibly many scripts in a subdirectory tree. This ...

find . -type f -path "./toto/*/titi/*/script.sh" -execdir bash script.sh \;

... does almost what I need, but it executes all the scripts it finds, even if some of them fail. I need something that stops finding and executing scripts as soon as one of those scripts fails.

I tried this variation:

find . -type f -path "./toto/*/titi/*/script.sh" -execdir "bash script.sh || exit 1 "\;

but it didn't do what I need.

How can I make find stop when one of the executed scripts fails?


Solution

  • The arguments to a find command represent a boolean expression that is evaluated with short-circuiting semantics. The default connective is "and", which can also be expressed explicitly with an -a argument (or -and in GNU find).

    When the command executed via an -exec or -execdir action exits with a non-zero status, that -execdir action evaluates to false. Otherwise, it evaluates to true. You can use this to control whether a -quit action is performed. For example:

    find . \
      -type f \
      -path "./toto/*/titi/*/script.sh" \
      \( -execdir bash ./script.sh \; -o -quit \)
    

    The -o is an "or" connective. The sub-expression serving as its right-hand is evaluated if and only if the left-hand operand evaluates to false.

    Note, however, that the execution of a -quit does not directly affect the exit status of find. With this approach, if you want also to detect whether any of the scripts failed then your best bet is to make the scripts emit some kind of recognizable output to their standard error (or maybe their standard output), which you then pipe into some other command for analysis. For example, if you can rely on the scripts to write a failure message containing the word "error" to their standard error if and only if they fail (in addition to terminating with a non-zero status), then you could try something along these lines:

    find . \
      -type f \
      -path "./toto/*/titi/*/script.sh" \
      \( -execdir bash ./script.sh \; -o -quit \) \
      2>&1 1>/dev/null |
      ! grep -q -i '\<error\>'
    
    

    As far as this attempt goes:

    find . -type f -path "./toto/*/titi/*/script.sh" -execdir "bash script.sh || exit 1 "\;
    

    it is not useful for this purpose to add || exit 1 to the command, because that will be executed only if the script already terminates with a non-zero exit status anyway. Failure of a command executed via an -exec or -execdir action affects the boolean value of that action, but it does not automatically make find terminate.