One problem users run into when first learning how to work with Linux is how to find the files they are looking for.
This guide will cover how to use the aptly named find
command. This will help you search for files on your system using a variety of filters and parameters. It will also briefly cover the locate
command, which can be used to search for files in a different way.
To follow along with this guide, you will need access to a computer running a Linux-based operating system. This can either be a virtual private server which you’ve connected to with SSH or your local machine. Note that this tutorial was validated using a Linux server running Ubuntu 20.04, but the examples given should work on a computer running any version of any Linux distribution.
If you plan to use a remote server to follow this guide, we encourage you to first complete our Initial Server Setup guide. Doing so will set you up with a secure server environment — including a non-root user with sudo
privileges and a firewall configured with UFW — which you can use to build your Linux skills.
Note: To illustrate how the find
and locate
commands work, the example commands in this guide search for files stored under /
, or the root directory. Because of this, if you’re logged into the terminal as a non-root user, some of the example commands may include Permission denied
in their output.
This is to be expected, since you’re searching for files within directories that regular users typically don’t have access to. However, these example commands should still work and be useful for understanding how these programs work.
The most obvious way of searching for files is by their name.
To find a file by name with the find
command, you would use the following syntax:
- find -name "query"
This will be case sensitive, meaning a search for query
is different from a search for Query
.
To find a file by name but ignore the case of the query, use the -iname
option:
- find -iname "query"
If you want to find all files that don’t adhere to a specific pattern, you can invert the search with -not
:
- find -not -name "query_to_avoid"
Alternatively, you can invert the search using an exclamation point (!
), like this:
- find \! -name "query_to_avoid"
Note that if you use !
, you must escape the character with a backslash (\
) so that the shell does not try to interpret it before find
can act.
You can specify the type of files you want to find with the -type
parameter. It works like this:
- find -type type_descriptor query
Here are some of the descriptors you can use to specify the type of file:
f
: regular filed
: directoryl
: symbolic linkc
: character devicesb
: block devicesFor instance, if you wanted to find all of the character devices on your system, you could issue this command:
- find /dev -type c
This command specifically only searches for devices within the /dev
directory, the directory where device files are typically mounted in Linux systems:
Output/dev/vcsa5
/dev/vcsu5
/dev/vcs5
/dev/vcsa4
/dev/vcsu4
/dev/vcs4
/dev/vcsa3
/dev/vcsu3
/dev/vcs3
/dev/vcsa2
/dev/vcsu2
/dev/vcs2
. . .
You can search for all files that end in .conf
with a command like the following. This example searches for matching files within the /usr
directory:
- find /usr -type f -name "*.conf"
Output/usr/src/linux-headers-5.4.0-88-generic/include/config/auto.conf
/usr/src/linux-headers-5.4.0-88-generic/include/config/tristate.conf
/usr/src/linux-headers-5.4.0-90-generic/include/config/auto.conf
/usr/src/linux-headers-5.4.0-90-generic/include/config/tristate.conf
/usr/share/adduser/adduser.conf
/usr/share/ufw/ufw.conf
/usr/share/popularity-contest/default.conf
/usr/share/byobu/keybindings/tmux-screen-keys.conf
/usr/share/libc-bin/nsswitch.conf
/usr/share/rsyslog/50-default.conf
. . .
Note: The previous example combines two find
query expressions; namely, -type f
and -name "*.conf"
. For any file to be returned, it must satisfy both of these expressions.
You can combine expressions like this by separating them with the -and
option, but as this example shows the -and
is implied any time you include two expressions. You can also return results that satisfy either expression by separating them with the -or
option:
- find -name query_1 -or -name query_2
This example will find any files whose names match either query_1
or query_2
.
find
gives you a variety of ways to filter results by size and time.
You can filter files by their size using the -size
parameter. To do this, you must add a special suffix to the end of a numerical size value to indicate whether you’re counting the size in terms of bytes, megabytes, gigabytes, or another size. Here are some commonly used size suffixes:
c
: bytesk
: kilobytesM
: megabytesG
: gigabytesb
: 512-byte blocksTo illustrate, the following command will find every file in the /usr
directory that is exactly 50 bytes:
- find /usr -size 50c
To find files that are less than 50 bytes, you can use this syntax instead:
- find /usr -size -50c
To find files in the /usr
directory that are more than 700 Megabytes, you could use this command:
- find /usr -size +700M
For every file on the system, Linux stores time data about access times, modification times, and change times.
Access Time: The last time a file was read or written to.
Modification Time: The last time the contents of the file were modified.
Change Time: The last time the file’s inode metadata was changed.
You can base your find
searches on these parameters using the -atime
, -mtime
, and -ctime
options, respectively. For any of these options, you must pass a value indicating how many days in the past you’d like to search. Similar to the size options outlined previously, you can prepend these options with the plus or minus symbols to specify “greater than” or “less than”.
For example, to find files in the /usr
directory that were modified within the last day, run the following command:
- find /usr -mtime 1
If you want files that were accessed less than a day ago, you could run this command:
- find /usr -atime -1
To find files that last had their meta information changed more than 3 days ago, you might execute the following:
- find /usr -ctime +3
These options also have companion parameters you can use to specify minutes instead of days:
- find /usr -mmin -1
This will give the files that have been modified in the last minute.
find
can also do comparisons against a reference file and return those that are newer:
- find / -newer reference_file
This syntax will return every file on the system that was created or changed more recently than the reference file.
You can also search for files by the user or group that owns the file using the -user
and -group
parameters, respectively. To find every file in the /var
directory that is owned by the syslog user run this command:
- find /var -user syslog
Similarly, you can specify files in the /etc
directory owned by the shadow group by typing:
- find /etc -group shadow
You can also search for files with specific permissions.
If you want to match an exact set of permissions, you use can this syntax specifying the permissions using octal notation:
- find / -perm 644
This will match files with exactly the permissions specified.
If you want to specify anything with at least those permissions, you can precede the permissions notation with a minus sign:
- find / -perm -644
This will match any files that have additional permissions. A file with permissions of 744
would be matched in this instance.
In this section, you will create an example directory structure that you’ll then use to explore filtering files by their depth within the structure.
If you’re following along with the examples in this tutorial, it would be prudent to create these files and directories within the /tmp/
directory. /tmp/
is a temporary directory, meaning that any files and directories within it will be deleted the next time the server boots up. This will be useful for the purposes of this guide, since you can create as many directories, files, and links as you’d like without having to worry about them clogging up your system later on.
After running the commands in this section, your /tmp/
directory will contain three levels of directories, with ten directories at the first level. Each directory (including the temporary directory) will contain ten files and ten subdirectories.
Create the example directory structure within the /tmp/
directory with the following command:
- mkdir -p /tmp/test/level1dir{1..10}/level2dir{1..10}/level3dir{1..10}
Following that, populate these directories with some sample files using the touch
command:
- touch /tmp/test/{file{1..10},level1dir{1..10}/{file{1..10},level2dir{1..10}/{file{1..10},level3dir{1..10}/file{1..10}}}}
With these files and directories in place, go ahead and navigate into the test/
directory you just created:
- cd /tmp/test
To get a baseline understanding of how find
will retrieve files from this structure, begin with a regular name search that matches any files named file1
:
- find -name file1
Output./level1dir7/level2dir8/level3dir9/file1
./level1dir7/level2dir8/level3dir3/file1
./level1dir7/level2dir8/level3dir4/file1
./level1dir7/level2dir8/level3dir1/file1
./level1dir7/level2dir8/level3dir8/file1
./level1dir7/level2dir8/level3dir7/file1
./level1dir7/level2dir8/level3dir2/file1
./level1dir7/level2dir8/level3dir6/file1
./level1dir7/level2dir8/level3dir5/file1
./level1dir7/level2dir8/file1
. . .
This will return a lot of results. If you pipe the output into a counter, you’ll find that there are 1111
total results:
- find -name file1 | wc -l
Output1111
This is probably too many results to be useful to you in most circumstances. To narrow it down, you can specify the maximum depth of the search under the top-level search directory:
- find -maxdepth num -name query
To find file1
only in the level1
directories and above, you can specify a max depth of 2 (1 for the top-level directory, and 1 for the level1
directories):
- find -maxdepth 2 -name file1
Output./level1dir7/file1
./level1dir1/file1
./level1dir3/file1
./level1dir8/file1
./level1dir6/file1
./file1
./level1dir2/file1
./level1dir9/file1
./level1dir4/file1
./level1dir5/file1
./level1dir10/file1
That is a much more manageable list.
You can also specify a minimum directory if you know that all of the files exist past a certain point under the current directory:
- find -mindepth num -name query
You can use this to find only the files at the end of the directory branches:
- find -mindepth 4 -name file1
Output./level1dir7/level2dir8/level3dir9/file1
./level1dir7/level2dir8/level3dir3/file1
./level1dir7/level2dir8/level3dir4/file1
./level1dir7/level2dir8/level3dir1/file1
./level1dir7/level2dir8/level3dir8/file1
./level1dir7/level2dir8/level3dir7/file1
./level1dir7/level2dir8/level3dir2/file1
. . .
Again, because of the branching directory structure, this will return a large number of results (1000).
You can combine the min and max depth parameters to focus in on a narrow range:
- find -mindepth 2 -maxdepth 3 -name file1
Output./level1dir7/level2dir8/file1
./level1dir7/level2dir5/file1
./level1dir7/level2dir7/file1
./level1dir7/level2dir2/file1
./level1dir7/level2dir10/file1
./level1dir7/level2dir6/file1
./level1dir7/level2dir3/file1
./level1dir7/level2dir4/file1
./level1dir7/file1
. . .
Combining these options like this narrows down the results significantly, with only 110 lines returned instead of the previous 1000.
find
ResultsYou can execute an arbitrary helper command on everything that find
matches by using the -exec
parameter using the following syntax:
- find find_parameters -exec command_and_options {} \;
The {}
is used as a placeholder for the files that find
matches. The \;
lets find
know where the command ends.
For instance, assuming you’re still in the test/
directory you created within the /tmp/
directory in the previous step, you could find the files in the previous section that had 644
permissions and modify them to have 664
permissions:
- find . -type f -perm 644 -exec chmod 664 {} \;
You could also change the directory permissions in a similar way:
- find . -type d -perm 755 -exec chmod 700 {} \;
This example finds every directory with permissions set to 755
and then modifies the permissions to 700
.
locate
An alternative to using find
is the locate
command. This command is often quicker and can search the entire file system with ease.
You can install the command on Debian or Ubuntu with apt
by updating your package lists and then installing the mlocate
package:
- sudo apt update
- sudo apt install mlocate
On Rocky Linux, CentOS, and other RedHat derived distributions, you can instead use the dnf
command to install mlocate
:
- sudo dnf install mlocate
The reason locate
is faster than find
is because it relies on a database that lists all the files on the filesystem. This database is usually updated once a day with a cron script, but you can update it manually with the updatedb
command. Run this command now with sudo
privileges:
- sudo updatedb
Remember, the locate
database must always be up-to-date if you want to find new files. If you add new files before the cron script is executed or before you run the updatedb
command, they will not appear in your query results.
locate
allows you to filter results in a number of ways. The most fundamental way you can use it to find files is to use this syntax:
- locate query
This will match any files and directories that contain the string query
anywhere in their file path. To return only files whose names contain the query itself, instead of every file that has the query in the directories leading up to it, you can include the -b
flag to search only for files whose “basename” matches the query:
- locate -b query
To have locate
only return results that still exist in the filesystem (meaning files that were not removed between the last updatedb
call and the current locate
call), use the -e
flag:
- locate -e query
You can retrieve statistics about the information that locate
has cataloged using the -S
option:
- locate -S
OutputDatabase /var/lib/mlocate/mlocate.db:
21015 directories
136787 files
7727763 bytes in file names
3264413 bytes used to store database
This can be useful for getting a high-level understanding of how many files and directories exist on your system.
Both the find
and locate
commands are useful tools for finding files on your system. Both are powerful commands that can be strengthened by combining them with other utilities through pipelines, but it’s up to you to decide which tool is appropriate for your given situation.
From here, we encourage you to continue experimenting with find
and locate
. You can read their respective man
pages to learn about other options not covered in this guide, and you can analyze and manipulate search results by piping them into other commands like wc
, sort
, and grep
.
Spin up a real linux environment on a hosted virtual machine in seconds with DigitalOcean Droplets! Simple enough for any user, powerful enough for fast-growing applications or businesses.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Sign up for Infrastructure as a Newsletter.
Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.
The above need “file” to be changed to “file1” otherwise they get no hits
You can use KrojamSoft FilesSearch Tool, it helped me out on many cases. Hope this helped you out!
I think there is a typo in this section: http://d.pr/i/15yf3
It should have
-type f
and-type d
respectively.@wizardware: You can:
<ul><li>delete ~/test completely by running <code>rm -r ~/test</code>; or</li> <li>delete all of the files/directories in ~/test by running <code>rm -r ~/test/</code>; or</li> <li>delete all of the files/directories in ~/test that start with <strong>level</strong> by running <code>rm -r ~/test/level</code>;</li></ul>
Is there a fairly simple way to delete all of those files and directories we created in the “Filtering by Depth” section of this tutorial?
I’m still learning the basics of Linux, and a simple simple rm combined with rmdir will take quite a bit of time… Perhaps some sort of mv ‘target’ >> null approach?