Last updated: 28 April 2021

The find utility is used to search for files. As finding files is an important and common task the utility has a very large number of options. This article aims to give you and overview of how you can use find, and we will throw in a few tips and tricks along the way.

As an aside, when we talk about files in Linux we usually talk about any type of file, including directories, symbolic links and even sockets (as everything in Linux is a file). You can of course use find to find directories, as we will see very shortly.

Options

Before we look at the utility’s options we should mention that you can run find without any options. Just entering find displays all files (including directories and hidden files) below the currently directory. You can easily test this yourself by creating some directories and files:

$ mkdir css
$ touch index.html .gitignore css/global.css
$ find
.
./css
./css/global.css
./.gitignore
./index.html

The above example shows that, by default, find searches recursively from the current directory. The find command we just ran is therefore identical to the command find . (or, if you prefer, find ./).

Recursive control

The -maxdepth option limits how far find descends into the directory tree. If you want to limit your search to just the current working directory then you can use -maxdepth 1:

$ find -maxdepth 1
.
./css
./index.html
./.gitignore

And if you want to include one level of subdirectories then you can change the value to 2:

$ find -maxdepth 2
.
./css
./css/global.css
./index.html
./.gitignore

As you can see, the second command also found files inside the css directory.

Long listings

If you want find to return more than just file names you can add -ls at the end of your command. It seems counter-intuitive to add -ls at the end of the command but there is a logical reason: it is an expression rather than an option. The expression tells find what should be done with found files. The -ls expression shows a detailed listing. There are a few other common options, including -delete and -exec. We will encounter them later, but first we should look at -ls:

$ find ./ -ls
1694651881    0 drwx------   3 example example       72 Sep 22 01:40 .
144431381     0 drwx------   2 example example       23 Sep 22 01:30 ./css
144431384     0 -rw-r--r--   1 example example        0 Sep 22 01:30 ./css/global.css
1694651888    0 -rw-r--r--   1 example example        0 Sep 22 01:23 ./.hidden
1694651890    0 -rw-r--r--   1 example example        0 Sep 22 01:30 ./index.html
1694651891    0 -rw-r--r--   1 example example        0 Sep 22 01:30 ./.lvimrc
1694651895    4 -rw-r--r--   1 example example      475 Sep 22 01:40 ./find

The output includes the inode number and is therefore similar to the output you get with ls -liR.

Finding files by name

The -name option lets you search for files by name. You can use globbing:

$ find ./ -name index.htm*
./index.html

The above command searches for files matching the pattern index.htm*. The asterisk is a “glob” that matches zero or more characters. So, index.htm* matches index.html (and it would also match index.htm).

Also, the -name option is case-sensitive. You can do a case-insentitive search using the -iname option.

Finding files by type

The -type option searches for files by type. The most common types are regular files (f) and directories (d):

$ find -type f -name .gitignore
./.gitignore

$ find -type d -name css
./css

Finding files by size

The -size option lets you search for files by size. The most common file size units are k (kilobytes), M (megabytes) and G (gigabytes). By default, the -size option searches for files of exactly the specified size, which isn’t all that useful. You can search for files that are smaller or larger than a certain size using the minus (-) and plus (+) signs. For example, the below command finds files larger than 1GB in the /home/example directory:

$ find /home/example/ -size +1G
/home/example/backups/backup_20210101.tar.gz
/home/example/backups/backup_20210201.tar.gz
…

Finding files by ownership

The -user and -group options let you search for files by ownership. This is useful to find files owned by a particular user or to check if files in a certain directory are as you expect. For example, you can retrieve files in a website directory owned by apache:

$ find /var/www/html/example.net/ -user apache
/var/www/html/example.net/sites/default/files
/var/www/html/example.net/sites/default/files/.htaccess
…

Using operators

You can make searches more specific using the -and, -or and -not operators. In the below example we are looking for files that are not owned by apache and not owned by example. Rather than listing the files we pipe the output to wc -l, which gives us a count of the number of lines returned by the command. There are zero lines, which in this case is good:

$ find /var/www/html/example.net/ -not -user apache -and -not -user example | wc -l
0

The -not operator can be substituted by an exclamation mark (!):

$ find /var/www/html/example.net/ ! -user apache -and ! -user example | wc -l
0

Another interesting option is -nouser. As the name suggests, you can use this to find files that have no user (perhaps because the user has been deleted):

$ find ./ -nouser

Finding files by permission

The -perm option lets you search for files by permission. You can use this to check if permissions are as you expect in, say, a website directory. Here, we search for directories (-type d) that don’t have 750 permissions (! -perm 750):

$ find /var/www/html/example.net/ -type d ! -perm 750
/var/www/html/example.net/sites/default/files
…

You can also use the -executable option to find files that are executable:

$ find /var/www/html/example.net/ -type f -executable | wc -l
0

Finding files by timestamp

The -ctime, -mtime and -atime options let you search for files by timestamp:

  • ctime is the date (and time) a file’s attributes were last updated.
  • mtime is the date a file was last modified.
  • atime is the date a file was last accessed.

You can search for files that have (or have not) been modified in the last x days by preceded the value with either a plus (+) or minus (-) sign. A plus sign returns files that have not been updated during the number of days you specify. You can use this to check if a directory contains old files that can be deleted. Here, we are checking a logs directory for files that are more than a year old:

$ find /home/example/logs/ -mtime +365 | wc -l
143

You can use the minus sign to find files that have recently been modified. For instance, you can search for files in your bin directory that have been modified in the last five days:

$ find bin/ -type f -mtime -5
bin/backup.sh

You can use the -cmin, -mmin and -amin options if you prefer to specify minutes rather than days. For instance, if you just ran a chmod command then you can check which files have been affected:

$ find ./ -cmin -1

Finding files modified on a specific date

In modern versions of Bash you can also specify dates using the -newermt option. You can use the option instead of -mtime:

$ find /home/example/backups/ -type f -name "*.tar.gz" ! -newermt "2021-01-01"

Here, ! -newermt "2021-01-01" returns all files that are not newer than 1st January 2021. Note the exclamation mark, which negates the date passed to newermt and therefore only returns files that do not match.

In the same way you can find files that were modified on a certain day. For example, to find files that were modified on the 9th September 2020 you can use two -newermt options. The first specifies the date you want to find and the second uses the exclamation mark and the next day (in this case 10th September 2020):

$ find /home/example/ -type f -newermt "2020-09-09" ! -newermt "2020-09-10" -ls
1650859535      4 -rw-r--r--   1  example example     3412 Sep  9 12:25 ./public-html/wp-config.php

Executing commands

Earlier, we talked about the -ls expression. There are a few other common expressions. A common expression is -exec. The expression is used to execute a command on the files found by find. You need to specify the command you want to run (obviously) followed by a pair or curly brackets ({}) and either an escaped semi-colon (\;) or plus sign (\+).

To illustrate, the following command finds files that have no owner. It uses -exec to run the command stat "%U %n", which prints the owner and file name:

$ find ./ -nouser -exec stat -c "%U %n" {} \;
UNKNOWN ./sites/default/files
UNKNOWN ./sites/default/files/assets
UNKNOWN ./sites/default/files/assets/images
UNKNOWN ./sites/default/files/assets/images/blog

The curly brackets are a place-holder for the files retrieved by find and the semi-colon indicates that there are no further arguments for the command. The semi-colon has to be escaped; otherwise the shell might expect a next command (because you can delimit multiple commands with semi-colons).

The above command is relatively ineffective because the stat command (stat -c "%U %n") is executed on each file found by find. In effect, it is executed like so:

stat -c "%U %n" ./sites/default/files
stat -c "%U %n" ./sites/default/files/assets
stat -c "%U %n" ./sites/default/files/assets/images
stat -c "%U %n" ./sites/default/files/assets/images/blog

Piping the output from find to xargs would be more efficient as all the files would be passed to the stat command as a string with file names. It is possible to do exactly that with -exec by substituting the semi-colon with a plus sign:

$ find ./ -nouser -exec stat -c "%U %n" {} \+
UNKNOWN ./sites/default/files
UNKNOWN ./sites/default/files/assets
UNKNOWN ./sites/default/files/assets/images
UNKNOWN ./sites/default/files/assets/images/blog

The output is exactly as before but the command is executed as a single command. In effect, it is run like so:

stat -c "%U %n" ./sites/default/files ./sites/default/files/assets ./sites/default/files/assets/images ./sites/default/files/assets/images/blog

Using -exec with the plus sign is therefore largely identical to using xargs. You can argue that -exec uses a somewhat awkward syntax. However, a nice feature of -exec is that you don’t have to worry about file names containing spaces. It is up to you to decide which of the following commands makes the most sense:

$ find ./ *.txt -exec file {} \+
$ find ./ *.txt -print0 | xargs -0 file

Deleting files

The -exec option is often used to remove files, as in:

$ find ./ -type f -name *.bak -exec rm {} \+

A slightly better way of removing files is by using the -delete expression. Using this option should be a little quicker for the reason that find doesn’t have to call the external rm utility.

$ find ./ -type f -name *.bak -delete

More information

Hopefully, this article has given you a good idea of how you can use find. To learn more, it is worth reading the man page. The best way to get comfortable with utilities such as find is by using them though. You will quickly get familiar with the various options we covered, and you will almost certainly find other options that are relevant for your workflow.