home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Unix Power ToolsUnix Power ToolsSearch this book

9.9. 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 (Section 13.2) 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.

The syntax is peculiar and in many cases, it is simpler just to pipe the output of find to xargs (Section 28.17). 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 (the semicolon). But since the shell uses the semicolon 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 to mimic the -print operator:

% find . -exec echo {} \;

The C shell (Section 29.1) uses the characters { and }, 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 under the current directory, you could execute:

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

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

% 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 with filenames that contain embedded spaces. (Versions of xargs that support the -0 option can avoid this problem; they expect NUL characters as delimiters instead of spaces, and find's -print0 option generates output that way.)

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, and use the -inum operator with -exec 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 to confirm the action first 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 it 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 that I tell find to execute.

NOTE: GNU find will replace the {} in {}.orig for you. If you don't have GNU find, a little Bourne shell while loop with redirected input 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 read the filenames from standard input then make them available as $file, one by one.

Section 9.12 and Section 9.27 have more examples of -exec.

-- BB



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.