RSS Feed Subscribe to RSS Feed

 

find

“find” is a unix command-line tool for locating files (and directories). The results can be displayed, passed to another command (e.g. grep, ls etc, see more below), or the find command has its own limited set of actions that can be performed too, such as delete.

Find allows you to specify all manner of search criteria such as name, location, size, permissions, modify date etc. Using regex expressions with those criteria makes it more flexible still.

See the full find manual here.

Syntax

The basic syntax of the find command is:

find path options

Example:

find . -name test.txt 2>/dev/null

Path

The path to search. Note that find is recursive by default so it will also search all sub-directories (use “-maxdepth 1″ to limit to only one directory level). Some examples:

ExampleDescription
ExampleDescription
find . -name test.txtsearch for files called test.txt in the current directory (and all sub directories)
find /mydir -name test.txtSearch for all files called test.txt in ‘/mydir’ directory (and as always all sub directories)
find /dir1 /dir2 -name test.txtSearch multiple directories
find ./dir*1 -name text.txtSearch using regex. I don’t personally do this approach a lot, but this example would find files called test.txt in subdirectories called dir1, directions1 or directory1 etc
find . -name "*.java" | xargs grep -l bobsearch for files with .java extension in the current directory (and all sub directories) that contain the text bob. The -l means only the filenames are printed.

Options

The options argument, or “search expression” is taken to be the first argument starting with “-” and can contain several parts. By default these are AND, but can be treated as OR by prefixing with -or.

For example, this will  find all files ending in .pdf or .txt

find . -name “*.txt” -or -name “*.pdf”

Some useful options include:

  • -type
    • Limits the types of files returned. For example d (directories), l (symbolic links) and f (regular files)
    • Examples:
      • find . -type d -name “test*” ##finds directories starting with test
  • -size
    • file size; use k for Kilobytes, M for Megabytes or ‘G’ for Gigabytes
    • Examples
      • find . -size +10k #find files over 10Kb
      • find . -size -10M #find files less than 10 Mb
  •  Exclusions
    • You can exclude file of a certain name. For example, this will find all .txt files not called a.txt:
      • find . -name *.txt ! -name ‘a.txt’
  •  Time
    • Background notes
      • It’s somewhat of a side note, but in unix you can access the following times about a file
        • Access: last time the contents of the file were examined.
        • Modify: Last time the contents of the file were changed.
        • Change: Last time the file’s inode was changed.
      • The change time includes things like modifying the permissions and ownership, while the modify time refers specifically to the files contents.
      • It is the modify time is the time that shows up when you do “ls -l”, and is the one I use most often.
    • -mmin n
      • File’s data was last modified n minutes ago
      • Examples:
        • find . -mmin -10 #find files modified less than 10 minutes ago
        • find . -mmin +10 #find files modified more than 10 minutes ago
        • find . -mmin 10 #find files modified exactly 10 minutes ago (not often useful)
    • -mtime n
      • File’s  data was last modified n days ago
      • Examples:
        • find . -mtime +10 #find files modified more than 10 days ago
    • -newermt
      • See more here
      • Examples:
        • find . -type f -newermt 2007-06-07 ! -newermt 2014-02-14 #finds files modified on 2014-02-14

 

Ignoring errors

find will display an error message for each directory it tries to operate on for which you don’t have read permissions. These are usually noise you want to ignore. To do so, use “2>/dev/null”. For example:

find / -name foo 2>/dev/null

See more on /dev/null here.

 

Passing the results to another command

It can sometimes be very useful to pass the find results off to another command, such as grep, ls, chmod, rm, mv etc. There are two ways (that I know of) to do this. xargs and -exec.

-exec

-exec is an option of the built in actions of the find command itself (along with -print, -delete etc) and allows you pass the results of the find command (i.e. a list of files) to another command.

For example, this will add write perms for the current user to all

find . -name “*.txt”  -exec chmod u+x {} \;

“-exec grep” specifies that for every found file, it should run grep command, passing its filename as an argument to it, by replacing {} with the filename.

The \; is simply terminating the exec statement.

You can also terminate the statement with + and this has the advantage that the specified command (chmod in this example) will run with a list of the files from find, as opposed to running once for every file from find. Using this approach, the total number of invocations of the command will be much less than the number of matched files, and hence be faster. I generally use the + terminator by default.

Examples:

find . -name “*.txt”  -exec ls -l {} \;

find . -mtime -10 -exec grep -l quick {} + 2>/dev/null #find all files modified in the last 10 days containing the text “quick”

xargs

xargs is a command used to execute commands based on arguments from standard input.

Before the introduction of the + terminator for -exec, xargs had the big advantage of allowing you to pass the list of the files from find to the next command. However, terminating -exec with + now achieves the same result, so the distinction between exec terminated with a  + and using xargs is much less clear.

There are some discussions about using find with xargs versus exec + here and here. Basically, there seems to be some subtle differences when unusual files names are involved (e.g. containing spaces or starting with dashes), but for the most part the 2 approaches are the same. Some examples of some of the above commands rewritten using xargs are:

  • find . -name “*.txt”  | xargs chmod u+x
  • find . -name “*.txt” | xargs ls -l
  • find . -mtime -10 | xargs grep -l quick 2>/dev/null

I generally prefer xargs over -exec simply because the syntax for xargs seems a little better (no need for the {} to represent the file name, or to terminate the command), but again the differences are subtle.

find vs grep -r

grep -r (where -r means recursive) is a fine alternative to using find and passing the results to grep. However, find is a much more powerful tool than the simple recursive search of grep -r. Both can filter on name, but with find you can utilize its whole plethora options such as filtering on certain files/directories using regex, limiting to certain types of files and certain file attributes. grep -r is useful, but find and grep combined are a very powerful combination.

I tend to favor grep by itself when I can, for simplicity’s sake, but switch to combining find and grep when the extra power of find is required to limit the files affected.

 

 

Tags: , , , ,

Leave a Reply