type options to find. It is more in how you use them. prints all objects named test. The man page states that the - print option is always true. The meaning only becomes clear if you write something like this:
find . -name test -print -exec rm {} \; | | |
| |
This command prints qualified pathnames, and because the -print option returns a true, the boolean expression continues, so that the -exec option is also executed, deleting files named test. If there were two objects, one is a file, the other a directory, we might get the following output:
./backup/test | | |
rm: ./backup/test directory | |
./survey/test | | |
because you cannot rm a directory.
If we reversed the order of the options: we get a better (more correct) result:
find . -name test -exec rm {} \; -print | | |
| |
we get a better (more correct) result:
rm: ./backup/test directory | | |
./survey/test | |
Now the list only includes the files for which the -exec worked (though we get an error message from rm about those objects it could not remove). We can get rid of the error message with better qualifying:
find . -name test -type f -exec rm {}\; -print | | |
| |
This command now reads something like: "print the names of files deleted that have the name test." If the file cannot be deleted, the pathname will not be -print'd (but you will get an error if the command - exec'd generates one).
An example of when you might want to use - print before - exec (instead of after):
find . -name test -type f -print -exec cat {} \; | | |
| |
To -exec or to not -exec
The -ok option is similar to -exec, except that it prompts and waits for an ok before executing the command:
% find . -name test -ok cat {} \; | | /td> |
< cat="" ...="" ./test="">? y | |
hi from this dir | | |
< cat="" ...="" ./survey/test="">? n | | |
% | | |
This is a rather useful option if you are issuing a destructive command such as rm, and cannot perfectly qualify objects to be acted on.
Find also makes a nice front end to cpio. You can develop any number of qualifiers, then supply the -cpio option and have those objects written to the supplied device:
find /design -user fredm -cpio /dev/rmt/s0d0 | |
| |
Because we now know that find pipes command lines to a shell for execution, it is rather obvious that something like this:
find . Ðname '*.bak' Ðexec rm {} \; | |
| |
could result in many processes being created. If we tried this in a korn shell, it might work, but only if there were not that many files found by find. If there were lots of them, we would have probably exceeded maximum string length, and gotten an error.
The solution is a simple one. Pipe the output of find to xargs. The xargscommand understands maximum string length, and makes sure it does not exceed it. Here is an example:
find . Ðname '*.bak' | xargs rm | |
| |
In this case, xargs makes sure that the rm command is fed as many filenames as possible without exceeding maximum string length.
Many people write a script or program when find would have performed the job easily. In more difficult cases, find does not have enough qualifying options to do exactly what people might need done. Well, if you really understand the discussion above about -exec, you should have noticed that find is extensible. Because all options to find are a boolean expression, you can use - execas a qualifier, supplying a script as the command to be executed. The script must complete with 0 status (successful) if the "test" it contains passes.
As an example, suppose we want to locate all Perl scripts on our system (and assuming they all contain '#!/usr/local/bin/perl' as the first line in the script.) If we were to use this find line:
find / -type f -perm -1 -print | |
| |
we would end up with all owner executable files: binaries, shell and Perl scripts alike. If all Perl scripts were named ending in .pl life would be good:
find / -name '*.pl' -type f -perm -1 -print | |
| |
But who names all Perl scripts that way? The solution (obviously) is to extend find to handle Perl scripts. I might do this with a simple shell script as follows (yeah yeah, it could have been a Perl script):
#!/usr/bin/ksh | | |
head -1 $1 | grep -q '#! | | |
*/usr/local/bin/perl' | | |
This script looks at the first line of the file passed to it as a command line argument ($1), to see if it is a Perl script. The grep command returns 0 if a match is found, thus meeting the requirements of find. Assuming the script is in ~/bin named testit, we might try something like:
find / -type f -perm -1 -exec ~/bin/testit {} \; -print | |
| |
The problem with this is one of speed. We will be opening and looking in many files, so this will take quite some time.
In the meantime, I think I'll go look for my cell phone.