How do I execute multiple commands when using find?

In this demonstration, I'll use sh -c 'echo first; false' (or true) for the first -exec. That will give some output and also have the selected exit code effect. Then echo second will be used for the second one. Assume that there's one file in the current directory.

$ find . -type f -exec sh -c 'echo first; false' \; -exec echo second \;
first
$ find . -type f -exec sh -c 'echo first; true' \; -exec echo second \;
first
second
$ find . -type f \( -exec sh -c 'echo first; false' \; -false -o -exec echo second \; \)
first
second
$ find . -type f \( -exec sh -c 'echo first; false' \; -false -o -exec echo second \; \)
first
second

A real command of this type would look like:

find . -type f \( -exec command1 \; -false -o -exec command2 \; \)

In the second set, the escaped parentheses group the two -exec clauses. The -false between them forces the test state to "false" and the -o causes the next expression (the second -exec) to be evaluated because of the -false.

From man find:

expr1 expr2
Two expressions in a row are taken to be joined with an implied "and"; expr2 is not evaluated if expr1 is false.

expr1 -a expr2
Same as expr1 expr2.

expr1 -o expr2
Or; expr2 is not evaluated if expr1 is true.


If you don't matter about cmd1 being able to prevent cmd2 from being run due to the error code 0:

find . -exec cmd1 \; -exec cmd2 \;

The only reliable way to get both commands to always run this is to have find invoke a shell that will subsequently run the commands in sequence:

find . -exec bash -c 'cmd1; cmd2' filedumper {} \;

If you don't mind making a script that does cmd1 and cmd2, then it's just this:

find . -exec myscript {} \;

or

find . -exec myscript {} +

Use \; or + depending on whether myscript can handle more than one file at a time (gets hairy if there's spaces in filenames, etc).

Putting what you were doing in a script, in my experience, has almost always been worth it. If you have to do it once, you'll have to do it again.

But a neat trick is to put the find inside the script:

if [ $# -gt 0 ]; then
    for each; do
        touch $each
    done
else
    find . -exec $0 {} +
fi

Then myscript works on whatever files you give as arguments for it to work on, but if you don't give it any arguments, it runs find from the current dir, and then calls itself on the found files. This gets around the fact that find -exec can't call shell functions you've defined in your script.