FORTRAN
FORTRAN

Reputation: 637

How to exit from find -exec if it fails on one of the files

I want to run the command

find some/path -exec program \{} \; 

but I want the find command to quit as soon as the command

program \{}

fails on any of the files found.

Is there a simple way of doing this?

Upvotes: 33

Views: 9967

Answers (5)

Tomasz Gandor
Tomasz Gandor

Reputation: 8833

Here is my example for a "build system", which stops after hitting the first compiler error (based on Kojiro's answer, which did not exaclty work for me):

(The need for escaped parentheses is real. I know that hurts.)

find -name '*.cpp' \( -print -a -exec g++ -c {} \; -o -quit \)

I want to build a static library of basically all C++ files located in the current directory and below.

Before running the compiler I want to have the file -print-ed, then -exec-ed, but when it fails (and leaves errors on stderr, it should -quit.

-a is like && and -o is like || in shell or C.

Without the parentheses, GNU find "optimizes" the query, by trying the most probable condition first, which is -- I guess -- -quit.

Upvotes: 3

mikeserv
mikeserv

Reputation: 694

% _find_trap() {
>   _find_pid="${1}" ; _find_ops="${2}" ; _find_trigger="${3}"
>   shift 3 && set -- "${@}" 
>   trap 'kill -s INT "-${_find_pid}" \
>     unset _find_pid _find_ops _find_trigger ; set - \
>     1>&2 printf "%s" "find killed due to trap" \
>     exit [CODE] ' TRAP
>  while { sh -c "${_find_ops} ${@}"} {
>    [ "${_find_trigger}" ] && { kill -s TRAP "-${_find_pid}" ; break ; }
>    ...
>  }
> export -f _find_trap ; find . -execdir _find_trap \"$$\" \"${cmds}\" \
>   \"${testable_trigger}\" "{}" +

Upvotes: 0

kojiro
kojiro

Reputation: 77107

In addition to the other fine answers, GNU find (at least) has a -quit predicate:

find path -other -predicates \( -exec cmd {} \; -o -quit \)

The -quit predicate is certainly non-standard and does not exist in BSD find.

Upvotes: 8

user000001
user000001

Reputation: 33327

I think it is not possible to achieve what you want, only with find -exec.

The closest alternative would be to do pipe find to xargs, like this:

find some/path -print0 | xargs -0 program

or

find some/path -print0 | xargs -0L1 program

This will quit if program terminates with a non-zero exit status

  • the print0 is used so that files with newlines in their names can be handled
  • -0 is necessary when -print0 is used
  • the L1 tells xargs program to execute program with one argument at a time (default is to add all arguments in a single execution of program)

If you only have sane file names, you can simplify like this:

find some/path | xargs program

or

find some/path | xargs -L1 program

Finally, If program takes more than one argument, you can use -i combined with {}. E.g.

find some/path | xargs -i program param1 param2 {} param4

Upvotes: 19

Eugene Yarmash
Eugene Yarmash

Reputation: 149776

You could pipe the output from find to another subprocess and use while/break:

find some/path | while read f
do
    program $f
    if [ $? -ne 0 ]
    then
        break
    fi
done

Upvotes: 1

Related Questions