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"