1. Introduction
The Linux find command can be used to find files and directories on a disk. It provides several command-line options that make it a powerful tool. In this tutorial, we’ll look at how to use the find command.
2. Syntax
Let’s quickly take a look at the basic syntax of the find command:
find [path...] [expression]
Both path and expression are optional.
The path argument specifies one or more directories to search. The default is the current working directory.
The expression argument is what determines which files and directories to include in the output, as well as what action to take on them. The default is to print all non-hidden files and directories.
We’ll take a closer look at expressions in the next section.
Note that different Linux distributions and versions may use slightly different syntax or offer different options.
3. Expressions
The expression argument is made up of options, tests, and actions. A single expression can combine any number of these using traditional boolean operators such as and and or.
Let’s take a look at each of these in more detail.
3.1. Options
Options affect the overall operation of find, rather than the processing of a specific file during the search.
Some of the most important options are:
- -d, -depth: performs a depth-first traversal by processing subdirectories before files in the directory itself
- -daystart: measure time from the beginning of the day instead of 24 hours ago
- -help: prints a simple command-line usage and then exits
- -mindepth, -maxdepth: controls how many directory levels to search before stopping (default mindepth is 0, and maxdepth defaults to unlimited)
3.2. Tests
Tests are the core of the find command. Applied to each file that is found, tests return true or false depending on whether that particular file passes or not.
We can use tests to look at various file attributes such as modified times, pattern matches, permissions, size, and more. Let’s look at some of the more popular tests we can perform.
First, there are tests for matching files by name or type:
- -name: tests if the file name matches a pattern (uses a simple pattern match and only looks at the file name)
- -regex: tests if the file name matches a pattern (uses standard Emacs regular expressions and looks at full file path)
- -type: tests if the file is a specific type (regular file, directory, symbolic link, etc.)
Let’s use find with the -name test to find all XML files in the current directory:
> find . -name "*.xml"
src/main/resources/applicationContext.xml
src/test/resources/applicationContext-test.xml
Notice the default output is simply the full path of each file.
Now, let’s find only directories in the /tmp directory:
find /tmp -type d
There are also several tests that can match files using time comparisons:
- -amin, -anewer, -atime: tests the last access time of the file against a relative time or another file
- -cmin, -cnewer, -ctime: tests the created time of the file against a relative time or another file
- -mmin, -mnewer, -mtime: tests the modified time of the file against a relative time or another file
- -newer: tests if the file is newer than another file
Here’s an example find command that uses –ctime to find all JAR files created in the past year in a directory named lib:
find lib -name "*.jar" -ctime -365
Or we can find all files in the current directory that are newer than a file named testfile:
find . -newer testfile
A few other handy tests can match based on other file properties like permissions or size:
- -perm: tests if the file permissions match a given permissions mode
- -size: tests the size of the file
Here, we’ll use -perm to find all files in the current directory that match the permission mode 700:
find . -perm 700
And let’s use -size to find all files larger than 1 kilobyte in a directory named properties:
find properties -size 1k
3.3. Actions
Actions are executed on files that match all tests. The default action is to simply print the file name and path.
There are a few other actions we can use to print more details about the matching files:
- -ls: perform a standard directory listing of the file
- -print, -print0, -printf: print the details of the to stdout
- -fprint, -fprint0, -fprintf: print details of the file to a file
To demonstrate, let’s use the -ls action to perform a directory listing of all .jar files in the target directory:
> find target -name "*.jar" -ls
4316430646 88112 -rw-r--r-- 1 mike staff 45110374 Oct 14 15:01 target/app.jar
And we can use -printf with a format string to print only the file size and name on each line:
> find lib -name "*.jar" -printf '%s %p\n'
12345 file1.jar
24543 file2.jar
Some of the more advanced actions we can use with the find command are:
- -delete: remove the file from disk
- -exec: execute any arbitrary command
Suppose we want to delete all .tmp files from the /tmp directory:
find /tmp -name "*.tmp" -delete
Or to find all .java files containing the word “interface”:
find src -name "*.java" -type f -exec grep -l interface {} \;
Notice the “;” on the end. This causes the grep command to be executed for each file one at a time (the “\” is required because semi-colons will be interpreted by the shell). We could also use a “+” instead, which would cause multiple files to be passed into grep at the same time.
An alternative to the -exec command is piping output to xargs. The xargs command takes items from the standard input and executes a given command on those inputs. Let’s rewrite the above command using xargs:
find src -name "*.java" -type f | xargs grep -l interface
While the results are the same, there is a significant difference in execution speed. Let’s time the -exec version:
time find src -name "*.java" -type f -exec grep -l interface {} \;
which returns:
8.39s user
20.43s system
84% cpu
34.048 total
Now, if we time the use of xargs:
time find src -name "*.java" -type f | xargs grep -l interface
We see:
find src -name "*.java" -type f
0.13s user
0.96s system
70% cpu
1.552 total
xargs grep -l interface
0.62s user
0.42s system
58% cpu
1.794 total
Using xargs ends up being much faster. The increase in speed is because xargs operates essentially on a batch of the input, the size of which is determined by xargs itself, whereas -exec executes grep on each result from find, one at a time.
3.4. Operators
All of the expression types above can be combined using traditional boolean operators.
Here’s a quick list of the supported operators, in order of precedence:
- (expr): parenthesis force the execution of the expression inside before any other expression; be careful to avoid shell interpolation by using quotes
- !, -not: negates an expression; be careful to avoid shell interpolation by using quotes
- -a, -and: performs a boolean and operation on two expressions, returning true only if both expressions are true
- -o, -or: performs a boolean or operation on two expressions, returning true if either expression is true
For example, we can find any file that is not a directory in the src directory:
find src ! -type d
Or we can find all files with either a .xml or .yaml extension in the properties directory:
find properties -name "*yaml" -o -name "*.xml"
4. Advanced Options
In addition to the path and expressions, most versions of find offer more advanced options:
find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path] [expressions]
First, the -H, -L, and -P options specify how the find command handles symbolic links. The default is to use -P, which means file information of the link itself will be used by tests.
The -O option is used to specify a query optimization level. This parameter changes how find re-orders expressions to help speed up the command without changing the output. We can specify any value between 0 and 3 inclusive. The default value is 1, which is sufficient for most use cases.
Finally, the -D option specifies a debug level — it prints diagnostic information that can help us diagnose why the find command is not working as expected.
5. Conclusion
In this tutorial, we’ve seen how to use the Linux find command.
By using a combination of expressions and boolean logic, the find command can help us locate files and directories efficiently.
Simply put, the find command is powerful enough on its own, but when combined with other Linux commands, it is one of the most useful command-line tools available.