[Chapter 17] 17.10 Running Commands on What You Find
Author: Jerry Peek, Tim O'Reilly & Mike Loukides
Date: 1998-08-04T21:38:23Z

#

| Chapter 17 Finding Files with find |

## 17.10 Running Commands on What You Find

[Often, when you find a file, you don't just want to see its name; you want to do something, like grep for a text string. To do this, use the -exec operator. This allows you to specify a command that is executed upon each file that is found. -TOR ]

The syntax is peculiar and in many cases, it is simpler just to pipe the output of find to xargs (17.2). However, there are cases where -exec is just the thing, so let's plunge in and explain its peculiarities.

The -exec operator allows you to execute any command, including another find command. If you consider that for a moment, you realize that find needs some way to distinguish the command it's executing from its own arguments. The obvious choice is to use the same end-of-command character as the shell (i.e., the semicolon). Since the shell uses the semicolon (8.5) itself, it is necessary to escape the character with a backslash or quotes.

Therefore, every -exec operator ends with the characters `\;`. There is one more special argument that find treats differently: `{}`. These two characters are used as the variable whose name is the file find found. Don't bother rereading that last line. An example will clarify the usage. The following is a trivial case, and uses the -exec operator with echo (8.6) to mimic the -print operator:

>     % find . -exec echo {} \;

The C shell uses the characters `{` and `}` (9.5), but doesn't change `{}` together, which is why it is not necessary to quote these characters. The semicolon must be quoted, however. Quotes can be used instead of a backslash:

>     % find . -exec echo {} ';'

as both will sneak the semicolon past the shell and get it to the find command. As I said before, find can even call find. If you wanted to list every symbolic link in every directory owned by a group staff, you could execute:

    `...`
    % find `pwd` -type d -group staff -exec find {} -type l -print \;

To search for all files with group-write permission and remove the permission, you can use:

    -perm
    % find . -perm -20 -exec chmod g-w {} \;

or:

>     % find . -perm -20 -print | xargs chmod g-w

The difference between -exec and xargs is subtle. The first one will execute the program once per file, while xargs can handle several files with each process. However, xargs may have problems (9.22) with filenames that contain embedded spaces.

Occasionally people create a strange file that they can't delete. This could be caused by accidentally creating a file with a space or some control character in the name. find and -exec can delete this file, while xargs could not. In this case, use ls -il to list the files and i-numbers (1.22), and use the -inum operator with -exec (23.16) to delete the file:

>     % find . -inum 31246 -exec rm {} ';'

If you wish, you can use -ok which does the same as -exec, except the program asks you first to confirm the action before executing the command. It is a good idea to be cautious when using find, because the program can make a mistake into a disaster. When in doubt, use echo as the command. Or send the output to a file and examine the file before using the file as input to xargs. This is how I discovered that find requires `{}` to stand alone in the arguments to -exec. I wanted to rename some files using `-exec mv {} {}.orig` but find wouldn't replace the `{}` in `{}.orig`. I learned that I have to write a shell script (17.11) that I tell find to execute. [A little Bourne shell while loop (44.10) with redirected input (45.23) can handle that too:

    >
    $ find ... -print |
    > while read file
    > do mv "$file" "$file.orig"
    > done

find writes the filenames to its standard output. The while loop and its read command (44.13) read the filenames from standard input, then make them available as `$file`, one by one. -JP ]

Articles 17.12 and 17.24 have more examples of -exec.

- BB

17.9 Problems with -newer |  | 17.11 Using -exec to Create Custom Tests