Lost And Find
I just got back from a trip to Fiji. I had lost the company cell phone and had to golooking for it. This all started when I was asked 'where did you lose it'? Some quickthinking allowed me to take a huge boondoggle looking for it. Because I had never been toFiji before, I suppose the phone could not have been there.
Amusing that I don't mind looking all over the world for my phone, yet I get upset whenpeople use the find command to search the entire disk. They should know what directoriesthey have been using and limit the search to those locations. Why search the whole diskwhen you've only been working under your home directory and one or two other directories.
Of course, when looking for a lost phone, you never know where you might have lost it,so I am heading for Seattle next. Maybe Argentina after that. But I must admit: I had ahard time learning to use find. The reason is that it reminds me of a 6th grade Englishteacher, always complaining about predicates and verbs and other grammatical stuff. Itmust have been written by an English major.
The man page was not much help to me, thus I have developed my own way to describe it.Here goes:
First, learn that find is a very powerful command. It can do much more than find lostfiles. More accurately, it will search a tree structure, testing each object to see if itmeets a set of criteria, and if so, perform one or more actions. The set of criteria isactually a boolean expression, thus allowing us to apply quite a set of criteria andactions. This means that find, once learned, can replace many shell scripts.
I feel that the best way to look at the command synopsis is:
|find <where> <what> <action> |
The list of locations for find to search immediately following the command, forexample: find ~ted /tmp /design/data
tells find to search three directories. This is what I meant by 'you should be able tolimit the search to probable directories. If I was looking for an operating systemconfiguration file, I might look in /etc, /opt/etc, and /var rather that in all of /.
After the where, comes the powerful part. The 'what to look for' section takes manyoptions. Lets start with the commonly known -name option, which allows us to specify apattern that the desired file must match. Suppose I had a large home directory and wantedto search for a file named "phone" that I created somewhere in the treestructure:
This is an incomplete command because I have not specified an action. HP-UX will dosomething reasonable (list the pathnames that match), but most versions of UNIX will not,so lets supply the default action in case you are running on (gasp) something other thanHP-UX:
|find ~ -name phone -print |
This command reads: Look in my home directory (~), for a file named 'phone', and printthe pathname for all objects by that name. Notice that I said objects by that name, notfiles. Since I was looking for a file named 'phone', a more accurate list would have beencreated by this command :
|find ~ -name phone -type f -print |
Here we have specified that the name must be 'phone', and the type must be a file, nota directory, or link, or character file, etc. Another way to write the same command wouldbe:
|find ~ -name phone -a -type f -print |
This is an important point to learn if you want to create very discriminating sets ofmatching criteria. The -a option means a boolean and. That is, the -name and the -typeoptions must both be true, in order for the -print action to happen. Anytime you list twooptions in a row, there is an implied 'and' between them.
You can also supply patterns to the -name option instead of just fixed names, forexample: find . -name '*phone*' -print
means look in the current directory for any object that has 'phone' as part of thename, rather than exactly 'phone' as the name. Note that any wildcard characters used (*in this example) must be escaped from the shell. The easiest way is to use single quotes.
If you are using find to develop a list of pathnames that you will be just looking at,or editing, there is no need to be overly discriminating. If you are automating a task,you should be as discriminating as possible, so that the list of files acted on are asaccurate as possible. There are many options that can be used to weed out objects that arenot what you want. Sometimes this requires a boolean expression using the 'or' (-o) or'not' (!) operators.
The following example looks for objects in the /etc directory with names ending in'save':
|find /etc -name '*save*' -print |
The results of the command on my system are shown in italics.
If were were using find to create a list of names, and did not want to generate anydirectory pathnames, yet allow all other object types (files, links, or several othertypes of objects) we could use the logical negate ('not'):
|find /etc -name '*save*' ! -type d -print |
Note that if you were using either of the C-shell shells (csh or tcsh) this would work,though most people would have escaped the ! so that the shell would not think it was ahistory invoke:
|find /etc -name '*save*' \! -type d -print |
Because there was a space after it, an escape after the exclamation mark, it was notnecessary. Sometimes you need to use the 'or' operator to supply multiple possibilities.The easiest example of this is using the -name option. The following find command couldnever list any objects due to the implied 'and' between the two -name options:
|find /etc -name test -name '*init' -print |
An object cannot be named 'test' and end with 'init'. Using the -o option does notsolve this issue:
|find /etc -name test -o -name '*init' -print |
This is again due to the implied 'and' before the -print option. Since the 'and' hashigher precedence than 'or', this actually reads 'In /etc', if you find an object named'test', do nothing, or, if you find an object named ending in 'init', (and) perform a-print. Amusingly enough, this will work on HP-UX but not on most other systems:
|find /etc -name test -o -name '*init' |
The real solution (meaning the one that can be supplied to other tasks with find) is touse the grouping operator to control precedence. Parenthesis are the highest precedence,the problem is that you must escape them from the shell, since they have meaning to theshell also. Here is a consistent (but ugly) way to write this:
|find /etc \( -name test -o -name '*init' \) -print |
This reads: if an object is named 'test' or ends in 'init', perform a -print. Usingparenthesis allows us to write things like:
|find /etc \( -name test -o -name '*init' \) -type f -print |
Which requires that both of the matching the names must be files, not links ordirectories. If we show the implied 'and' operators, it would look like this:
|find /etc \( -name test -o -name '*init' \) -a -type f -a -print |
|find /etc \( -name test -o \( -name '*init' -type f \) \) -print |
means that any object with a name of 'test', or files that end in 'init' will beprinted. A final warning about using parenthesis is that you must put spaces both beforeand after each escaped parenthesis, or you will end up with an error such as:
|find /etc \(-name test -o -name '*init'\) -print |
find: bad option -o
We will have to save discussing some more ways to qualify objects, and more powerfulactions that find can perform until next month.
My plane is waiting. Got to go find that cell phone.