How to get the exit code of commands started by find?

Using Stephen Kitt's suggestion in comments:

find . -type f -iname "*.sh" -exec sh -c 'for n; do ./testScripts.sh "$n" || exit 1; done' sh {} +

This will cause the sh -c script to exit with a non-zero exit status as soon as testScript.sh does. This means that find will also exit with a non-zero exit status:

If terminated by a plus sign, the pathnames for which the primary is evaluated are aggregated into sets, and utility will be invoked once per set, similar to xargs(1). If any invocation exits with a non-zero exit status, then find will eventually do so as well, but this does not cause find to exit early.


Regarding the questions in comment:

  1. for n; do ... ; done looks weird but makes sense when you realize that without anything to iterate over, the for loop will iterate over "$@" implicitly.

  2. The trailing sh at the end will be placed in $0 of the sh -c shell. The {} will be substituted by a number of pathnames. Without sh there, the first pathname would end up in $0 and would not be picked up by the loop, since it's not in $@. $0 usually contains the name of the current interpreter (it will be used in error message produced by the sh -c shell).


xargs will exit with an exit status between 1 and 125 (123 with GNU xargs), if any of the command fails, and will abort if any fails with a 255 status.

To use xargs reliably on the output of find (with -print0) and preserve the command's stdin, you'd need GNU xargs though. So, with GNU xargs and a shell with support for process substitution like ksh, zsh or bash:

xargs -n1 -r0a <(find . -type f -iname '*.sh' -print0) sh ./testScripts.sh

Or to abort at the first failing one:

xargs -r0a <(find . -type f -iname '*.sh' -print0) sh -c '
  for file do
    sh ./testScripts.sh "$file" || exit 255
  done' sh

You can also abort find upon the first error with (POSIX code):

find . -type f -name '*.[sS][hH]' -exec sh -c '
  for file do
    if ! sh ./testScripts.sh "$file"; then
      kill -s PIPE "$PPID"
      exit 1
    fi
  done' sh {} +

(using SIGPIPE as a less noisy signal with some shells like bash). That will cause find to be killed and so return with a non-zero exit status.

To get the exact value of the exit status of the (here last) failing commmand, with zsh or bash, you can also do:

ret=0
while IFS= read -rd '' -u3 file; do
  sh ./testScripts.sh "$file" 3<&- || ret=$?
done 3< <(find . -type f -iname '*.sh' -print0)

Though with zsh, you don't even need find for that:

set -o extendedglob
ret=0
for file (./**/*(#i).sh(D.)) {
  ./testScripts.sh $file || ret=$?
}