find - find files according to certain criteria

find is a long-standing Unix utility. Its role is to recursively scan one
or more directories and find files which match a certain set of criteria
in these directories. Even though it is very useful, its syntax is truly
arcane, and using it requires a little use.

Quick Reference:
	find / -name nametofind
	find . -name nametofind
	find / |grep nametofind

The general syntax is:

find [options] [directories] [criterion] [action]

If you do not specify any directory, find will search the current directory.
If you do not specify the criterion, this is equivalent to "true", thus all
files will be found. The options, criteria, and actions are so numerous only
some are mention here.

Options:

  -xdev         - Do not search on directories located on other filesystems.
  -mindepth  - Descend at least  levels below the specified directory
                  before searching for files.
  -maxdepth  - Search for files which are located at most n levels below
                  the specified directory.
  -follow       - Follow symbolic links if they link to directories. By
                  default, find does not follow them.
  -daystart     - When using tests related to time (see below), take the
                  beginning of current day as a timestamp instead of the
                  default (24 hours before current time).

Criterion:

  A criterion can be one or more of several atomic tests.

  -type      - Search for a given type of file;  can be one of:
    f (regular file)
    d (directory)
    l (symbolic link)
    s (socket)
    b (block mode file)
    c (character mode file)
    p (named pipe)

  -name   - Find files which names match the given .
     With this option,  is treated as a shell globbing pattern.

  -iname  - Like -name, but ignore case.

  -atime , -amin  - Find files which have last been accessed  days
     ago (-atime) or  minutes ago (-amin). You can also specify + or
     -, in which case the search will be done for files accessed
     respectively at most or at least  days/minutes ago.

  -anewer  - Find files which have been accessed more recently than
     file 

  -ctime , -cmin , -cnewer  - Same as for -atime, -amin and
    -anewer, but applies to the last time when the contents of the file
    have been modified.

  -regex  - As for -name, but pattern is treated as a regular
    expression.

  -iregex  - As for -regex, but ignore case.

There are many other tests, refer to the man page for more details. To
combine tests, you can use one of:

   -a  - True if both  and  are true; -a is implicit,
    therefore you can type    ... if you want all tests ,
    , ... to match.
   -o  - True if either  or  are true, or both. Note that
     -o has a lower precedence than -a, therefore if you want, say, to match
     files which match criteria  or  and match criterion , you
     will have to use parentheses and write (  -o  ) -a . You
     must escape (disactivate) parentheses, as otherwise they will be
     interpreted by the shell!
  -not  - Inverts test , therefore -not  is true if  is
     false.

Action:


  -print          - Just prints the name of each file on standard output.
                    This is the default action if you don't specify any.
  -ls             - Prints the equivalent of ls -ilds on each file found
                    on the standard output.
  -exec  - Execute command  on each file found. The
                    command line  must end with a ;, which you
                    must escape so that the shell does not interprete it;
                    the file position is marked with {}. See the examples
                    of usage to figure this out.
  -ok    - Same as -exec but ask confirmation for each command.



Still here? OK, now let's practice a little, as it's still the best way
to figure out this monster. Let's say you want to find all directories in
/usr/share. Then you will type:

find /usr/share -type d

Suppose you have an HTTP server, all your HTML files are in
/home/httpd/html, which is also your current directory. You want
to find all files which contents have not been modified for a month. As
you got pages from several writers, some files have the html
extension and some have the htm extension. You want to link
these files in directory /home/httpd/obsolete. You will then
type:

find ( -name "*.htm" -o -name "*.html") -a -ctime -30 -exec ln {}
/home/httpd/obsolete ;[27]

Okay, this one is a little complex and requires a little explanation. The
criterion is this:

( -name "*.htm" -o -name "*.html" ) -a -ctime -30

which does what we want: it finds all files which names end either by
.htm or .html (( -name "*.htm" -o -name "*.html" )), and (-a) which have
not been modified in the last 30 days, which is roughly a month (-ctime -30).
Note the parentheses: they are necessary here, because -a has a higher
precedence. If there weren't any, all files ending with .htm would have
been found, plus all files ending with .html and which haven't been modified
for a month, which is not what we want. Also note that parentheses are
escaped from the shell: if we had put ( .. ) instead of ( .. ), the shell
would have interpreted them and tried to execute -name "*.htm" -o -name
"*.html" in a subshell...  Another solution would have been to put
parentheses between double quotes or single quotes, but a backslash here
is preferable as we only have to isolate one character.

And finally, there is the command to be executed for each file:

-exec ln {} /home/httpd/obsolete ;

Here too, you have to escape the ; from the shell, as otherwise the shell
interprets it as a command separator. If you don't do so, find will complain
that -exec is missing an argument.

A last example: you have a huge directory /shared/images, with all kind of
images in it. Regularly, you use the touch command to update the times of a
file named stamp in this directory, so that you have a time reference. You
want to find all JPEG images in it which are newer than the stamp file, and
as you got images from various sources, these files have extensions jpg,
jpeg, JPG or JPEG. You also want to avoid searching in directory old. You
want to be mailed the list of these files, and your username is john:

find /shared/images -cnewer     \
     /shared/images/stamp       \
     -a -iregex ".*\.jpe?g"     \
     -a -not -regex ".*/old/.*" \
     | mail john -s "New images"