63

I'm trying to run multiple commands on things I have found, how can I achieve this?

find . -exec cmd1; cmd2

does not seem to work; it instead runs cmd2 after cmd1 has been executed on every file.

5 Answers 5

42

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.

1
37

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 {} \;
2

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.

0

I have been trying both previous solutions without much luck. This one has worked nicely for me:

$ for i in `find . -exec echo {} \;`; do cmd1 $i; cmd2 $i; done
1
  • This won't work if the list of files is too large.
    – Alex
    Aug 11, 2017 at 18:51
-3

try command:

sudo find -name "*[!0-9][1-9].txt" -exec chmod 744 * {} \; -a -exec ls -l {} \; | sort | parallel ls -l
1
  • 4
    Eh, that's not a good idea. The * in the -exec part will expand to all files in the current directory. Especially when running as sudo.
    – slhck
    Nov 24, 2013 at 8:43

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .