The more you use UNIX, the more likely you'll end up losing track of where some of your files are. In UNIX, however, there's a cool tool to help you find them again.
The grep family can help you find files by their content. There are a lot of other ways to look for things in UNIX, and that's where the find command can help. This command has a notation that is completely different from all other UNIX commands: It has full-word options rather than single-letter options. Instead of -n pattern to match filenames, for example, find uses -name pattern. |
The general format for this command is to specify the starting point for a search through the file system, followed by any actions desired. The list of possible options, or flags, is shown in Table 18.1.
Option | Meaning |
-atime n | True if the file was accessed n days ago. |
-ctime n | True if the file was created n days ago. |
-exec command | Execute command. |
-mtime n | True if the file was modified n days ago. |
-name pattern | True if the filename matches pattern. |
Print names of the files found. | |
-type c | True if the file is of type c (as shown in Table 18.2). |
-user name | True if the file is owned by user name. |
The find command checks the specified options, going from left to right, once for each file or directory encountered. Further, find with any of the time-oriented commands can search for files more recent than, older than, or exactly the same age as a specified date, with the specifications −n, +n, and n, respectively. Some examples will make this clear.
At its simplest, find can be used to create a list of all files and directories below the current directory:
% find . -print . ./OWL ./OWL/owl.h ./OWL/owl ./OWL/owl.c ./OWL/simple.editor.c ./OWL/ask.c ./OWL/simple.editor.o ./OWL/owl.o lots and lots of output removed ./dead.letter ./who.is.who ./src.listing ./tmp.listing ./.wrongwords ./papert.article
To limit the output to just those files that are C source files (those that have a .c suffix), I can use the -name option before the -print option:
% find . -name "*.c" -print
./OWL/owl.c
./OWL/simple.editor.c
./OWL/ask.c
./OWL/handout.c
./OWL/WordMap/msw-to-txt.c
./OWL/WordMap/newtest.c
./OWL/feedback.c
./OWL/define.c
./OWL/spell.c
./OWL/submit.c
./OWL/utils.c
./OWL/parse.c
./OWL/sendmail.c
./owl.c
./src/calc.c
./src/info.c
./src/fixit.c
./src/massage.c
Using the -name option before the -print option can be very handy.
To find just those files that have been modified in the past seven days, I can use -mtime with the argument -7 (include the hyphen):
% find . -mtime -7 -name "*.c" -print
./OWL/owl.c
./OWL/simple.editor.c
./OWL/ask.c
./OWL/utils.c
./OWL/sendmail.c
If I use just the number 7 (without a hyphen), I will match only those files that were modified exactly seven days ago:
% find . -mtime 7 -name "*.c" -print
%
To find those C source files that I haven't touched for at least 30 days, I use +30:
% find . -mtime +30 -name "*.c" -print
./OWL/WordMap/msw-to-txt.c
./OWL/WordMap/newtest.c
./src/calc.c
./src/info.c
./src/fixit.c
./src/massage.c
With find, I now have a tool for looking across vast portions of the file system for specific file types, filenames, and so on.
To look across the /bin and /usr directory trees for filenames that contain the pattern cp, I can use the following command:
% find /bin /usr -name "*cp*" -print
/usr/diag/sysdcp
/usr/spool/news/alt/bbs/pcbuucp
/usr/spool/news/alt/sys/amiga/uucp
/usr/spool/news/comp/mail/uucp
/usr/spool/news/comp/os/cpm
/usr/spool/news/comp/protocols/tcp-ip
/usr/man/man4/tcp.4p
/usr/man/man8/tcpd.8l
/usr/man/cat3f/%unitcp.3f.Z
/usr/man/cat3f/unitcp.3f.Z
/usr/unsup/bin/cpio
/usr/unsup/gnu/man/man1/cccp.1
/usr/news/cpulimits
/usr/doc/local/form/cp
/usr/doc/local/form/cpio
/usr/doc/local/form/rcp
/usr/doc/uucp
This type of search can take a long time on a busy system. When I ran this command on my system, it took almost an hour to complete! |
To find a list of the directories I've created in my home directory, I can use the -type specifier with one of the values shown in Table 18.2. Here's one example:
% find . -type d -print
.
./OWL
./OWL/Doc
./OWL/WordMap
./.elm
./Archives
./InfoWorld
./InfoWorld/PIMS
./Mail
./News
./bin
./src
./temp
%
Letter | Meaning |
---|---|
d | Directory |
f | File |
l | Link |
To find more information about each of these directories, I can use the -exec option to find. Unfortunately, I cannot simply enter the command; the exec option must be used with {}, which will be replaced by the matched filename, and ; at the end of the command. (If the is left out, the C shell will interpret the ; as the end of the find command.) You also must ensure that there is a space between the {} and the ; characters.
% find . -type d -exec ls -ld {} ;
drwx------ 11 taylor 1024 Dec 10 14:13 .
drwx------ 4 taylor 532 Dec 6 18:31 ./OWL
drwxrwx--- 2 taylor 512 Dec 2 21:18 ./OWL/Doc
drwxrwx--- 2 taylor 512 Nov 7 11:52 ./OWL/WordMap
drwx------ 2 taylor 512 Dec 10 13:30 ./.elm
drwx------ 2 taylor 512 Nov 21 10:39 ./Archives
drwx------ 3 taylor 512 Dec 3 02:03 ./InfoWorld
drwx------ 2 taylor 512 Sep 30 10:38 ./InfoWorld/PIMS
drwx------ 2 taylor 1024 Dec 9 11:42 ./Mail
drwx------ 2 taylor 512 Oct 6 09:36 ./News
drwx------ 2 taylor 512 Dec 10 13:58 ./bin
drwx------ 2 taylor 512 Oct 13 10:45 ./src
drwxrwx--- 2 taylor 512 Nov 8 22:20 ./temp
The find command is commonly used to remove core files that are more than a few days old. These core files are copies of the actual memory image of a running program when the program dies unexpectedly. They can be huge, so occasionally trimming them is wise:
% find . -name core -ctime +4 -exec /bin/rm -f {} ;
%
There's no output from this command because I didn't use the -print at the end of the command. What it does is find all files called core that have a creation time that's more than four days ago and remove them.
You can use find to search for files, and you can use grep to search within files, but what if you want to search a combination? That's where xargs is helpful. |
A few days ago, I was working on a file that was computing character mappings of files. I'd like to find it again, but I don't remember either the filename or where the file is located.
First off, what happens if I use find and have the -exec argument call grep to find files containing a specific pattern?
% find . -type f -exec grep -i mapping {} ; typedef struct mappings { map-entry character-mapping[] = { int long-mappings = FALSE; case 'l': long-mappings = TRUE; if (long-mappings) /** do a short mapping **/ /** do a long mapping **/ /** Look up the specified character in the mapping database **/ while ((character-mapping[pointer].key < ch) && (character-mapping[pointer].key > 0)) if (character-mapping[pointer].key == ch) return ( (map-entry *) &character-mapping[pointer]); # map,uucp-map = The UUCP Mapping Project = [email protected] grep -i "character*mapping" * */* */*/* to print PostScript files produced by a mapping application that runs on the bionet.genome.chromosomes Mapping and sequencing of eucaryote chromosomes. ./bin/my.new.cmd: Permission denied typedef struct mappings { map-entry character-mapping[] = { int long-mappings = FALSE; case 'l': long-mappings = TRUE; if (long-mappings) /** do a short mapping **/ /** do a long mapping **/ /** Look up the specified character in the mapping database **/ while ((character-mapping[pointer].key < ch) && (character-mapping[pointer].key > 0)) if (character-mapping[pointer].key == ch) return ( (map-entry *) &character-mapping[pointer]); or lower case values. The table mapping upper to
The output is interesting, but it doesn't contain any filenames!
A second, smarter strategy would be to use the -l flag to grep so that grep specifies only the matched filename:
% find . -type f -exec grep -l -i mapping {} ;
./OWL/WordMap/msw-to-txt.c
./.elm/aliases.text
./Mail/mark
./News/usenet.alt
./bin/my.new.cmd: Permission denied
./src/fixit.c
./temp/attach.msg
That's a step in the right direction, but the problem with this approach is that each time find matches a file, it invokes grep, which is a very resource-intensive strategy. Instead, you use the xargs to read the output of find and build calls to grep (remember that each time a file is seen, the grep program will check through it) that specify a lot of files at once. This way, grep is called only four or five times even though it might check through 200 or 300 files. By default, xargs always tacks the list of filenames to the end of the specified command, so using it is as easy as can be:
% find . -type f -print | xargs grep -l -i mapping
./OWL/WordMap/msw-to-txt.c
./.elm/aliases.text
./Mail/mark
./News/usenet.alt
./bin/my.new.cmd: Permission denied
./src/fixit.c
./temp/attach.msg
This gave the same output, but it was a lot faster.
What's nice about this approach to working with find is that because grep is getting multiple filenames, it will automatically include the filename of any file that contains a match when grep shows the matching line. Removing the -l flag results in exactly what I want:
% ^-l^ find . -type f -print | xargs grep -i mapping ./OWL/WordMap/msw-to-txt.c:typedef struct mappings { ./OWL/WordMap/msw-to-txt.c:map-entry character-mapping[] = { ./OWL/WordMap/msw-to-txt.c:int long-mappings = FALSE; ./OWL/WordMap/msw-to-txt.c: case 'l': long-mappings = TRUE; ./OWL/WordMap/msw-to-txt.c: if (long-mappings) ./OWL/WordMap/msw-to-txt.c: /** do a short mapping **/ ./OWL/WordMap/msw-to-txt.c: /** do a long mapping **/ ./OWL/WordMap/msw-to-txt.c: /** Look up the specified character in the mapping database **/ ./OWL/WordMap/msw-to-txt.c: while ((character- mapping[pointer].key ./src/fixit.c: /** do a long mapping **/ ./src/fixit.c: /** Look up the specified character in the mapping database **/ ./src/fixit.c: while ((character-mapping[pointer].key < ch) && ./src/fixit.c: (character-mapping[pointer].key > 0)) ./src/fixit.c: if (character-mapping[pointer].key == ch) ./src/fixit.c: return ( (map-entry *) &character- mapping[pointer]); ./temp/attach.msg:or lower case values. The table mapping upper to
When used in combination, find, grep, and xargs are a potent team to help find files lost or misplaced anywhere in the UNIX file system. I encourage you to experiment further with these important commands to find ways they can help you work with UNIX. |