You know find(1). It's that useful UN*X utility for locating files based on various criteria and doing things to them (usually in conjunction with xargs). But did you know you can write programs in it?
Here is a selection of find(1) programs I have written:
The traditional first program, this one is trivial enough to include here:
find . -path . -printf 'Hello, World\n' -prune
This was the first program I wrote, as an entry for the 99 Bottles of Beer on the Wall site (which gives listings of programs in an enormous number of different languages, all of which print the "lyrics" to "99 Bottles of Beer"). find(1) was the most obscure thing I could think of...
This is a modification of the beer hack, which fits into a four-line signature suitable for use in mail or news. The lyrics have been heavily abbreviated to make it fit, and as a result the singular/plural distinction has disappeared.
This one accomplishes the more significant programming task of printing all the primes under 100. The invocation of a second find as a sort of subroutine is quite neat IMHO...
Since this one looks more like line noise than most, you might want to try a version with comments that attempt to explain how it works...
The programs here follow a certain pattern. They are written as
/bin/sh
scripts to provide an easy way to run them and
to ensure that the shell handles quoting in the way we expect.
There's also a little boiler plate to create a directory to work in
and delete it after the find script has run. Usually we need to use
expr to do the arithmetic. But the flow control is entirely due to find.
The basic flow control structure with find programs works like this:
find . -noleaf # boilerplate code -path . [initialisation code here] -o -name [x] # or some other condition selecting on the filename [code] -name [y] [code] ...where the [code] sections mkdir a subdirectory of the directory currently being worked on. find will then obligingly recurse into that directory and reexecute the commands.
The expr expression "{} : '.*/\(.*\)'" is an idiomatic way of getting the basename of the current directory using expr's pattern matching operator.
The -noleaf
option tells find not to use an optimisation
where it works out the number of subdirectories of a directory without
having to actually read that directory. This doesn't take account of
subdirectories created during the execution of find, which causes all
these scripts not to work. If your find doesn't have -noleaf
it probably doesn't have the optimisation either; try simply omitting it.