This is a read-only archive. Find the latest Linux articles, documentation, and answers at the new Linux.com!

Linux.com

Feature

CLI Magic: Searching with find

By Paul Hudson and Andrew Hudson on July 10, 2006 (8:00:00 AM)

Share    Print    Comments   

The find command is one of the darkest and least understood areas of Linux, but it is also one of the most powerful. The biggest problem with find is that it has more options than most people can remember -- it truly is capable of doing most things you could want.

This article is excerpted from the newly published book Red Hat Fedora 5 Unleashed . Excerpt provided by Sams Publishing.

Admittedly, the find command does not help itself by using X-style parameters. The Unix standard is -c, -s, and so on, whereas the GNU standard is --dosomething, --mooby, and so forth. X-style parameters merge the two by having words preceded by only one dash. The most basic usage is this:

find -name "*.txt"

That query searches the current directory and all subdirectories for files that end in .txt. The previous search finds files ending in .txt but not .TXT, .Txt, or other case variations. To search without case sensitivity, use -iname instead of -name. You can optionally specify where the search should start before the -name parameter, like this:

find /home -name "*.txt"

Another useful test is -size, which lets you specify how big the files should be to match. You can specify your size in kilobytes and optionally also use + or - to specify greater than or less than. For example:

 find /home -name "*.txt" -size 100k 
 find /home -name "*.txt" -size +100k 
 find /home -name "*.txt" -size -100k 

The first brings up files of exactly 100KB, the second only files greater than 100KB, and the last only files less than 100KB.

The -user option enables you to specify the user that owns the files you are looking for. So, to search for all files in /home that end with .txt, are under 100KB, and are owned by user paul, you would use this:

find /home -name "*.txt" -size -100k -user paul

You can flip any of the conditions by specifying -not before them. For example, you can add a -not before -user paul to find matching files owned by everyone but paul:

find /home -name "*.txt" -size -100k -not -user paul

You can add as many -not parameters as you need, even using -not -not to cancel each other out! (Yes, that is pointless.) Keep in mind, though, that -not -size -100k is essentially equivalent to -size +100k, with the exception that the former will match files of exactly 100KB whereas the latter will not.

You can use -perm to specify which permissions a file should have for it to be matched. This is tricky, so read carefully. The permissions are specified in the same way as with the chmod command: u for user, g for group, o for others, r for read, w for write, and x for execute. However, before you give the permissions, you need to specify a plus, a minus, or a blank space. If you specify neither a plus nor a minus, the files must exactly match the mode you give. If you specify -, the files must match all the modes you specify. If you specify +, the files must match any the modes you specify. Confused yet?

The confusion can be cleared up with some examples. This next command finds all files that have permission o=r (readable for other users). Notice that if you remove the -name parameter, it is equivalent to * because all filenames are matched.

find /home -perm -o=r

Any files that have o=r set are returned from that query. Those files also might have u=rw and other permissions, but as long as they have o=r, they will match. This next query matches all files that have o=rw set:

find /home -perm -o=rw

However, that query does not match files that are o=r or o=w. To be matched, a file must be readable and writeable by other users. If you want to match readable or writeable (or both), you need to use +, like this:

find /home -perm +o=rw

Similarly, this next query matches files that are only readable by user, group, and others:

find /home -perm -ugo=r

Whereas this query matches files as long as they are readable by the user, or by the group, or by others, or by any combination of the three:

find /home -perm +ugo=r

If you use neither + nor -, you are specifying the exact permissions to search for. For example, the next query searches for files that are readable by user, group, and others but not writeable or executable by anyone:

find /home -perm ugo=r

You can be as specific as you need to be with the permissions. For example, this query finds all files that are readable for the user, group, and others and writeable by the user:

find /home -perm ugo=r,u=w

To find files that are not readable by others, use the -not condition, like this:

find /home -not -perm +o=r

Now, on to the most advanced aspect of the find command: the -exec parameter. This enables you to execute an external program each time a match is made, passing in the name of the matched file wherever you want it. This has very specific syntax: Your command and its parameters should follow immediately after -exec, terminated by \;. You can insert the filename match at any point using {} (an opening and a closing brace side by side).

So, you can match all text files on the entire system (that is, searching recursively from / rather than from /home as in our previous examples) over 10KB, owned by paul, that are not readable by other users, and then use chmod to enable reading, like this:

find / -name "*.txt" -size +10k -user paul -not -perm +o=r -exec chmod o+r {} \;

When you type your own -exec parameters, be sure to include a space before \;. Otherwise, you might see an error such as missing argument to ´-exec'.

Do you see now why some people think the find command is scary? Many people learn just enough about find to be able to use it in a very basic way, but hopefully you will see how much it can do if you give it a chance.

Share    Print    Comments   

Comments

on CLI Magic: Searching with find

Note: Comments are owned by the poster. We are not responsible for their content.

Bug in the examples!!!

Posted by: Anonymous Coward on July 10, 2006 05:18 PM
Actually the serach pattern for find command should be specified between single quotes like in:

find . -name '*.txt'

otherwise the pattern will be expanded by the shell and not by the find command and the examples will not work as expected

#

Re:Bug in the examples!!!

Posted by: Anonymous Coward on July 11, 2006 08:36 PM
Actually, that's not a bug. Double-quotes are sufficient to
prevent all shell expansions EXCEPT $ and `.
Wildcard * is not expanded within " ".
However, I tend to use ' ' for most things (unless I really want to do
command or variable substitution within a string).

#

Case insensitivity

Posted by: Anonymous Coward on July 10, 2006 06:07 PM
find can be case insensitive and that's very useful, especially on mounted samba shares. Just replace the "-name" parameter with "-iname"

find -iname MyFile
could return
myfile
MYFILE
MyFiLe<nobr> <wbr></nobr>...

#

Temporal search

Posted by: Anonymous Coward on July 10, 2006 08:04 PM
If you want to find out if any files got modified or created in the last 5 minutes (for example), then use:
<tt>find -mmin -5</tt>

#

.bashrc function

Posted by: Anonymous Coward on July 10, 2006 08:29 PM
i use these functions in my ~/.bashrc:

# Find a file with a pattern in name:
function ff() { find . -type f -iname '*'$*'*' -ls ; }

# Find a file with pattern $1 in name and Execute $2 on it:
function fe() { find . -type f -iname '*'$1'*' -exec "${2:-file}" {} \; ; }

i got it from
<a href="http://www.tldp.org/LDP/abs/html/sample-bashrc.html" title="tldp.org">http://www.tldp.org/LDP/abs/html/sample-bashrc.ht<nobr>m<wbr></nobr> l</a tldp.org>

#

Eh?

Posted by: Anonymous Coward on July 10, 2006 08:34 PM
Keep in mind, though, that -not -size -100k is essentially equivalent to -size +100k, with the exception that the former will match files of exactly 100KB whereas the latter will not.


Wouldn't -not -size 100k match all files not equal to 100k in size, including those smaller than 100k? Saying it's the equivalent of "greater than" 100k is not correct then.

#

Re:Eh?

Posted by: Anonymous Coward on July 11, 2006 12:27 AM
You missed the - before 100k on the first one.

#

How about xargs?

Posted by: Anonymous Coward on July 11, 2006 12:57 AM
I don't think any discussion of GNU find is complete without including xargs. Example:

find ~/Music -type f -iname "*.mp3" -print0 | xargs -0 mp3gain -r -k

would be able to troll your ~/Music folder for files named '*.mp3' or '*.MP3' (case insensitive is -iname) and run mp3gain on all those.

This approach is vastly superior to the -exec option in most cases because it only fires off one mp3gain process, which makes it much faster.

Maybe someone can refute my next claim; I found that adding -type f was almost a necessity, and that the operation was actually faster. Not a lot faster, but fast enough to justify typing it in. Anyone care to weigh in on that one?

#

Re:How about xargs?

Posted by: Anonymous Coward on July 11, 2006 08:18 AM

Yes, I have also found that <tt>xargs</tt> is very useful with <tt>find</tt>. The <tt>-type f</tt> restricts find's output to regular files; without that option, the names of all objects are printed, including directories, symlinks, etc. In your example, <tt>-type f</tt> is what you'd want, because <tt>mp3gain</tt> probably doesn't know what to do with directories or other types of objects. Or even worse, it might recursively act on all files within each directory given on its command line, which would cause files to be operated on multiple times! (Of course, in that case, you don't need <tt>find</tt> or <tt>xargs</tt>; just give <tt>mp3gain</tt> the directory name.)

Keep in mind that <tt>xargs</tt> has an idea of the maximum length of a command line, so it might run the command (i.e. <tt>mp3gain</tt>) multiple times. Sometimes that can cause different results than if the command is run only once.

Here is a cute trick when the command is <tt>grep</tt>: If you give that command multiple file names, it prepends each matched line with the file name, which is very useful. But if you're unlucky, it might happen that the last <tt>grep</tt> command is handed only one file name; you wouldn't see the file name for matched lines in that file. So what you can do is give the command as <tt>xargs grep pattern<nobr> <wbr></nobr>/dev/null</tt> which guarantees that <tt>grep</tt> will always be given at least two file names.

-- Larry Ruane

#

Re:How about xargs?

Posted by: Anonymous Coward on July 11, 2006 11:04 AM
It is ironic that you mention the GNU version of find and want to remind people about using xargs. You must not know that the GNU version of find has an "xargs eliminator" built in...<nobr> <wbr></nobr>:-)

View the man pages of find and look for "-exec command {} +"

Cheers

#

Re:How about xargs?

Posted by: Anonymous Coward on July 11, 2006 10:50 PM

Well, true enough, and an interesting feature. But xargs is more flexible. For example, if I want to grep files whose names end in ".txt" that occur in directories named "doc" found anywhere in a file system tree, then



<tt>find . -type f -name '*.txt' | grep '/doc/' | xargs grep pattern</tt>



is my friend. Note that the "." argument to find causes the pathnames to be prefixed with "./", which ensures that any "doc" directory at the top level of the tree is found.

#

Re:How about xargs?

Posted by: Anonymous Coward on July 13, 2006 03:45 AM
You could do that slightly more efficiently with:
<tt>find . -type f -path '*/doc/*.txt' | xargs<nobr> <wbr></nobr>...</tt>

#

Nice article

Posted by: Anonymous Coward on July 11, 2006 06:25 AM
Nice article, it teached me couple of things that I did not know.<nobr> <wbr></nobr>:)

Everybody: becareful with the -exec command!

#

ignoring permission errors

Posted by: Anonymous Coward on July 11, 2006 08:00 AM
Our systems allow find to know about directories it doesn't have the rights to search, resulting in many lines of errors for perhaps one correct result buried in them somewhere. To quell the errors we add this to the end of the find string:
2>/dev/null

#

Find a string within the files

Posted by: Anonymous Coward on July 11, 2006 08:03 AM
I frequently use find to locate files that contain a certain string.

find . -type f -iname '*.php' -exec grep -l -i "user" {} \;

This will locate all<nobr> <wbr></nobr>.php files containing the string 'user'.

Perhaps not monumental but it might help somebody.

#

Re:Find a string within the files

Posted by: Anonymous Coward on July 11, 2006 11:24 AM
You inadvertently nailed an egregious oversight by the authors: the "-iname" (case-insensitive) flag available to find.

Yet another reason not to consider Sam's as a worthwhile tech publisher.

#

Did you actually READ the artucle?

Posted by: Anonymous Coward on July 12, 2006 12:32 PM
the use of '-iname' instead of '-name' for case-insensitive name-matching is about the second point that he makes (right after explaining how '-name *.txt' won't find *.TXT, etc.)

#

Re:Find a string within the files

Posted by: Anonymous Coward on July 11, 2006 06:08 PM
You might as well use:
<tt>grep -iRl user *</tt>

Personnaly I have an alias in my<nobr> <wbr></nobr>.bashrc with:
<tt>alias g='egrep --color=auto -i -r' #> Quick search with colors, case insensitive and recurse dirs</tt>

Note that this doesnt change the normal grep behaviour:
<tt>cat<nobr> <wbr></nobr>/etc/passwd | g myself | less</tt>

I hope this helps...<nobr> <wbr></nobr>;-)

#

Re:Find a string within the files

Posted by: Anonymous Coward on July 11, 2006 06:16 PM
As you'd have guessed, it's
<tt>grep -iRl user *php</tt>
instead<nobr> <wbr></nobr>;-)

#

grep -ri user *.php

Posted by: Anonymous Coward on July 12, 2006 01:17 AM
To find all instances of user in *.php files.

#

find dangerous permissions

Posted by: Anonymous Coward on July 13, 2006 04:11 AM
find is also useful for finding dangerous permissions. unzip, in particular, seems to randomly ignore umask and create world-writable files and directories. It's also helpful to search for suid/sgid programs in odd places.

Most portably, you can do:
<tt>find / \! -type l \( -perm -04000 -o -perm -02000 -o -perm -01000 -o -perm -00002 -o -perm -00020 \) -ls</tt>
Eeek! What a mess! If you have GNU find, you can make it shorter:
<tt>find / \! -type l -perm +07022 -ls</tt>
... or use symbolic permissions:
<tt>find / \! -type l -perm +ugo+s,go+w -ls</tt>
These search for all files in the root directory and below, ignoring symbolic links, and matching set user id, set group id, sticky, world writable, or group writable permissions.

We exclude symbolic links because they are usually mode 0777 and would be false positives. The permissions of the link don't matter, only the target of the link. "-perm -N" means to match permissions including ALL bits of N. "-perm +N" means to match ANY bits of N. Some versions of find (e.g. BSD) don't have the -perm + option.

#

How do ya find omitting certain dirs..

Posted by: Anonymous Coward on July 14, 2006 01:31 AM
Im pretty good with GNU find, but for the life of me I can't figure out how to find and omit certain directories along the way. I know it involves prune but no example given to me previously (on irc) has worked.

#

Re:How do ya find omitting certain dirs..

Posted by: Anonymous Coward on July 14, 2006 08:30 AM
From the man page:
<tt>       ...To ignore a whole directory tree,  use  -prune  rather
than  checking every file in the tree.  For example, to skip the
directory `src/emacs' and all files and  directories  under  it,
and  print the names of the other files found, do something like
this:
    find . -path './src/emacs' -prune -o -print</tt>

You can omit multiple directories like this:
<tt>find . -path './a' -prune -o -path './b' -prune -o -print</tt>

#

CLI Magic: Searching with find

Posted by: Anonymous [ip: 60.242.98.215] on October 06, 2007 07:18 AM
How do you find files not matching a pattern. EG I wish to get a list of all the files not ending in *TXT.

#

This story has been archived. Comments can no longer be posted.



 
Tableless layout Validate XHTML 1.0 Strict Validate CSS Powered by Xaraya