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 are usually talking 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 dive into the options we should mention that you can run find without any options. Just entering find will display 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 will search 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 options lets you limit how far find should descend into subdirectories. If you want to limit your search to just the current working directory you can use -maxdepth 1:

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

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 subdirectory.

Long listings

If you want find to return more than just file names you can add the -ls option at the end of your command (which is rather counter-intuitive):

$ 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 of files 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. In the below example we search for files matching the pattern index.htm*, which matches index.html:

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

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

Finding files by type

The -type option lets you search 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), Mmegabytes and G (gigabytes). By default the -size option will search for files of exactly the size specified, 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_20190101.tar.gz
/home/example/backups/backup_20190201.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:

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

Here, we searched for directories (-type d) that don’t have 750 permissions (! -perm 750).

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

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

Finding files by timestamps

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

If you prefer to specify minutes rather than days you can use the -cmin, -mmin and -amin options. For instance, if you just ran a chmod command 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 that option instead of -mtime:

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

Here, ! -newermt "2019-01-01" returns all files that are not newer than 1st January 2019. 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 2019 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 2019):

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

Executing commands

The -exec option enables you to execute commands. The -exec option needs the command you wish to execute (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 and uses -exec to execute 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 we are executing. 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 differently. In effect, it is executed 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 option. Using this option should be a little quicker for the reason that find doesn’t have to call the external rm utility. Just be aware that the -delete option should be at the end of your find command:

$ 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.