How to use the find Command on Linux

Learn to find files from the terminal
Updated:

Finding files spread across a computer can be a challenge, GNU/Linux provides a number of classic Unix tools to help alleviate that problem. Today we will be focusing on the simplest of these tools: the find command.

First appearing in Unix Version 5 in 1974 find is a required standard in Unix-like operating systems (POSIX). On Linux find is provided by the GNU Findutils package.

find — search for files in a directory hierarchy

find is a tool for searching files by proprieties such as filename, file size, last changed, and much more By default find searches a directory and its subdirectories recursively for a given combination of file properties called and expression.

The layout of a typical find command is as follows:

find [starting directory] [search expression]

find will search the starting directory and all of its subdirectories for files that match a search expression, outputting matched files.

For example, here is a search that looks through a copy of the Emacs source code searching for Emacs Lisp files (.el) that are not a test file:

find ~/Downloads/emacs -name '*.el' -not -name '*-tests.el' -type

If no directory is given then the current directory is used, so the following is the same as the above command:

cd ~/Downloads/emacs
find -name '*.el' -not -name'*-tests.el' -type f

If no expression is given find will output all files and subdirectories in the given directory, similar to the tree command without the pretty formatting.

Expressions

find provides many different test expressions for searching file properties. A few useful ones are listed below, the complete list can be found in the find man page under Tests.

-name pattern find files where filename matches pattern
-path pattern find files where path matches pattern
-user username find files owned by a user
-readable match readable files
-writable match writable files
-perm mode match files with same permissions
-type c find files of a specific type (f regular file, l link, d directory, s socket)
-newer file find files newer that given file
-amin n find files that has been accessed n minutes ago
-atime n find files that has been accessed n days ago
-mmin n find files that has been modified n minutes ago
-mtime n find files that has been modified n days ago
-size n match file size (follow n with c for bytes, k for KiB, M for MiB, G for GiB)

Some important things to note:

  • Patterns should always be enclosed in 'single quotes' to avoid shell expansion

  • Pattern matching is case sensitive, case insensitive versions have a leading i e.g. -iname for case insensitive -name.

  • Any expression can search for the opposite by placing -not or ! before the test flag

  • Numeric arguments, denoted by n, can search for greater than +n, exactly n, or less than -n

By default back-to-back tests are logically ANDed, meaning the results must satisfy all of the restrictions given. These AND's can be explicitly stated with the -and or -a flag.

For example, if I was trying to search for formatted document files

find ~/Documents -name '*.doc' -name '*docx' -name '*.odt'

find returns no results as it is impossible for a file to end with 3 different extensions. We can fix this by ORing the tests with -or / -o files need to only match one test or the other.

find ~/Documents \( -name '*.doc' -o -name '*.docx' -o -name '*.odt' \) -type f

The parts of an expression enclosed with parenthesis are evaluated first. Parenthesis must be escaped '\( \)' as they have a special meaning in the shell (the '\' tells the shell to ignore them). In this example, the filename must match one of the file extensions AND be a regular file.

Patterns and Regular Expressions

Patterns are limited to a basic shell pattern matching scheme where:

* matches any combination of zero or more characters
? matches any one character
[STRING] match if character contained in string or a range [a-z] / [0-9]

find also supports using regular expressions with a number of different regex syntaxs supported, the default is Emacs style regular expressions.

A regex version of our document search would be:

find ~/Documents -regex '.*\.\(doc[x]?\|odt\)' -type f

Note that -regex operates on the file path and not just the filename.

Execute Commands on find Results

Now that you can get results from find you'll want to use them. Before piping the output of find consider the following: do you want to pipe "to the input of a program" or "to the arguments"? If it is the arguments piping will not work instead use the -exec flag.

Here's a command that moves recent photos from my SD card:

find "/mnt/SD" -iname '*.jpg' -ctime -2 -exec mv '{}' ~/Pictures/Lunch/ \;

In the -exec flag curly brackets '{}' will be replaced by the output from find. The '\;' denotes the end of the command, which again must be escaped as semicolons have a special meaning in the shell.

When ending -exec with a semicolon the command will be invoked once for every file found, very inefficient. A faster way would be append the results to a list of arguments once. This can be done in find with:

find ~/Project/work/ -regex '.*\.\(c\|h\)' -type f -writable -exec emacs '{}' \+

The big limitation of the append option is the curly brackets must be the last argument. For even more flexibility see the xargs command included with GNU findutils.

There is an interesting speed comparison of -exec and xargs options on everythingcli.org.

Delete Files

You can delete files directly from find with the -delete flag. Warning this is permanent and files will not be moved to the trash.

find ~/Downloads/ -maxdepth 1 \( -iname '*checksum*' -o -iname 'SHA[0-9]*' \) -delete

Deletes some old checksum files from the downloads folder.

Standards Compliance

Like most GNU utilities, GNU find provides many extra features that may not exist in other versions of find. If you intent to the find command in a cross platform script you should take this seriously. See the "Standards Conformance" section in the find(1) man page, as well as the POSIX find(1p) man page.

See Also