The Linux find command allows you to search for files and directories from the command line. It is a very adaptable utility that can be used with regular expressions and simple globbing wildcards. The find command has a plethora of built in tests, operators and functions. These allow you to search for file by name, date, size, ownership and even permissions. Although the find command is very powerful on it's own, it's usefulness can be further expanded by using the exec option. Here we will cover everything from simple syntax to execution of complex commands.
Table of Contents
Find Command Syntax and Structure
In the basic form, the find command takes options, a path to search, an expression to search for and an optional action to take when it finds a match.
find [options] [path...] [expression] [action]
Let's take a closer look at these, which will help fully understand how to use the find command.
Options
The find command only has five actual options (-H, -L, -P, -D, -O). If used they must appear before the first path.
- -P = Never follow symbolic links (default behavior)
- -L = Follow symbolic links
- -H = Do not follow symbolic links except while processing the command line arguments.
- -D = Print diagnostic information
- -O = Enable or set query optimization
Path(s)
A path is a starting point in the filesystem for the search. You can provide multiple paths and find will search each one recursively. Meaning, it will look inside all directories and sub-directories in the path provided.
Expressions
The expression is part of the command describes the criteria to match when searching for files. The expression can be a simple name, a wildcard or a very complex expression with logical control operators. Several expressions can be used to narrow your search results.
Actions
An action or set of actions to take on the files that are found or matched. This can be built in actions like delete, or expanded using xargs or exec. The default action is to print matched files to standard output (STDOUT).
Using Wildcards and Globbing
Any expression passed to the find command can use file globbing / wildcards. Although covering file globbing is outside the scope of this article, it is important that you know it is available. We will touch on how to use these concepts in this article. For more information read "Standard Wildcards and Globbing Patterns in Linux".
Built-in Tests for Find Command
The find command offers several built-in tests that help match files while searching. There are tests to match by name, file size, permissions and so on. In this section we will discuss the tests and show some examples on how to use them. In the following sections we will discuss how to use multiple tests to narrow your search and also how to take action on matched files.
Finding a File by Name
Find is most commonly used to find a file by name, or part of it's name. Using the -name switch, you can specify a name that you are looking for and find will search the provided path for a match.
Here is an example search for a file called Words.txt in the /usr directory.
$ find /usr -name Words.txt /usr/share/dict/Words.txt
By default the -name switch is case sensitive. In order to make it case insensitive you can change it to -iname.
$ find /usr -iname words.txt /usr/share/dict/Words.txt
As mentioned above, you can use wildcards to expand your search to multiple possible matches. For example, if you wanted to find all files with a .txt extension you can use the asterisks wildcard like so:
find /usr -iname '*.txt'
It is best to always escape or wrap wildcards in quotes. This will allow find to use the wildcards in it's search instead of it being expanded by the parent shell and throwing an error.
Finding Files by Type
You can use the -type switch to specify several different file types.
- b = block special
- c = character special
- d = directory
- p = named pipe
- f = regular file
- l = symbolic link
- s = socket
Although all of the above are supported, typically only f,d and l are popular options. Let's take a look at how to use those most popular file type searches.
In order to specify a type, simply call the -type switch followed by the letter of your choice.
For example, to find all regular files in your home directory, run the following command:
find ~/ -type f
To find all the directories in your home:
find ~/ -type d
Finding Files by Size
To find a file based on size, you can use the -size switch. The size option accepts several different units.
- b = 512-byte blocks (default)
- c = bytes
- w = two-byte words
- k = kilobytes (KiB, 1024 bytes)
- M = Megabyte (MiB)
- G = Gigabyte (GiB)
Along with the different units you also have greater than or less than options. These are denoted by a plus or minus sign before the size specification.
Finding files in your home directory larger than 50 MB:
find ~/ -size +50M
To find all files in /tmp that are smaller than 1 GB:
find /tmp -size -1G
Find all files that are 1MB in size:
find / -size 1M
This is very useful when dealing with full file systems and trying to find large files that are filling up a disk.
Finding Files by Owner or Group
The -user option for find allows you to search by file owner by using the -user switch followed by a username.
To find all files owned by a user named savona:
find / -user savona
You can also use the UID (User Identifier) of the user instead of their name. This is sometimes helpful when you delete a user but not their files.
To find all files owned by savona who has a UID of 1000:
find / -user 1000
The same principles can be applied to the group ownership. Instead of using the -user switch, we can use the -group switch.
To find all files owned by the wheel group:
find / -group wheel
You can use the id command to find the UID (User Identifier) and GID (Group Identifier) of the user.
$ id savona uid=1000(savona) gid=1000(savona) groups=1000(savona),981(libvirt)
You can find a list of groups on the system by simple reading the /etc/group file.
$ cat /etc/group root:x:0: bin:x:1: daemon:x:2: sys:x:3: adm:x:4: tty:x:5: disk:x:6: lp:x:7: ...OUTPUT TRUNCATED...
Finding Files Based on Date and Time
Linux systems record three attributes to files that deal with time.
- mtime = Modification time
- atime = Access time
- ctime = Change Time
If you are a Curious Putorius you are probably asking, what is the difference between Modification time and Change time. Well modification time is when the contents of the file were modified. For example if you had a text file and you opened it and changed a few lines. The change time is the same but tracks not only the files contents, but also it's metadata as well. So if you only changed the permissions on a file (not it's content) the change time would be updated but the modification time would not.
When searching by date and time, the default is to use days as the unit size. Just as with the -size switch you can use the greater than (+) and less than (-) operators. Let's take a look as some examples.
Find a file that was modified 10 days ago:
find / -mtime 10
To file a file that was modified in the last 30 days:
find / -mtime -30
Find a file that was modified more than 60 days ago:
find / -mtime +60
The mtime, ctime, and atime options all work exactly the same.
Find files that were accessed in the last 30 days:
find / -atime -30
As stated above, the default unit of measurement is days. Find offers a way to search by minutes for a more granular search. If you want to use minutes instead of days switch to the -amin, -cmin, and -mmin options. All of these options work exactly the same.
Here is an example of searching for files that were modified in the last 30 minutes.
find / -mmin -30
Finding Files Based on Permissions
The -perm switch will allow you to search for files based on their permissions. The permissions can be represented using symbolic or absolute (numeric) permission formats.
Let's say we wanted to search for a file that has read, write and execute for the user, read and execute for the group and read only for everyone else. This would be represented numerically 754 or symbolic as u=rwx,g=rx,o=r.
Here is how the find command would look to find a file with such permissions:
find / -perm 754
or
find / -perm u=rwx,g=rx,o=r
Using -perm in this manner will find all the files that match the permissions exactly.
For example, the following would find all files in which the user permissions are read, write and execute and no other permissions are set for group or everyone.
find / -perm u=rwx
or
find / -perm 700
The find command offers two additional forms of searching for permissions '/' or '-'. By placing either the forward slash or dash in front of the permission mode you can alter the results.
Using the dash search for permissions with ALL of the permission modes set.
If a forward slash is used, it searched for ANY of the permissions bits are set.
Here is an example. First we will search for files that have ALL the permission modes set using the dash.
$ find . -perm -777 ./sevensevenseven
Now we will search for a file that has ANY of the permission bits set using the forward slash.
$ find . -perm /777 . ./sevenfivetwo ./sevensevenseven
Find Executable Files
The -executable test can be used to match files that are executable and directories that are searchable by the current user.
Find all files in /usr/bin that are executable by current user:
find /usr/bin/ -executable
Find Writable or Readable Files
Similar to the other permission based tests, -readable and -writable allows you to easily find file that are readable or writeable by the current user.
To find all files in /usr/share/ that are readable by current user:
find /usr/share/ -readable
Find all files in /usr/share/ that are writable by current user:
find /usr/share/ -writable
Find Empty Files
Finding empty files is simple using the -empty test. Using the -empty switch will find all empty files (zero file size) and empty directories (no files or sub-directories).
Find all empty files and directories in /var/tmp:
find /var/tmp/ -empty
Find Files Based on SELinux Context
The -context test allows you to find files based on their SELinux context. On an SELinux enabled system all files and directories are labeled by context. You can see a files context by using the -Z option to the ls command.
Example of listing SELinux context:
$ ls -lZ total 4 drwxrwxr-x. 2 savona savona unconfined_u:object_r:user_home_t:s0 4096 Jul 28 16:07 ok -rwxr-x-w-. 1 savona savona unconfined_u:object_r:user_home_t:s0 0 Jul 28 15:03 sevenfivetwo -rwxrwxrwx. 1 savona savona unconfined_u:object_r:user_home_t:s0 0 Jul 28 15:03 sevensevenseven
Example of using the -context switch to find files of a specific SELinux context:
find ~/ -context unconfined_u:object_r:user_home_t:s0
You can use file globbing to make this easier, especially if you don't know the fill name of the context label.
find ~/ -context "*user_home*"
Using Multiple Expressions or Tests
The find command supports the use of multiple tests or expressions in order to narrow your search. Any of the above tests can be used in conjunction with other tests. Here are some examples.
Find an empty regular file (not directory) in your home directory:
find ~/ -type f -empty
Let's narrow it further by testing if the file is readable by the current user:
find ~/ -type f -empty -readable
Now let's add another test to only match files that have a specific SELinux context:
find ~/ -type f -empty -readable -context "*user_home*"
Now, let's narrow it further and only look for files with the .sql extension.
find ~/ -type f -empty -readable -context "*user_home*" -name "*.sql"
As you can see the find command can be very powerful, especially since you now know all the available tests.
Limiting Directory Depth
Using Maxdepth Option
By default the find command will recursively search all directories and sub-directories in the given path. Sometimes this returns a very large number of results, or takes a really long time to run. You can use the -maxdepth switch to tell it to stop after going x number of directories deep.
Tell find to stop 5 levels of directories deep:
find / -maxdepth 5
Using Mindepth Option
The mindepth option is a little different that you would think. The -mindepth switch tell find to not apply any tests or actions at levels less than x levels deep. For example if you had a directory that had 10 levels of sub-directories under it, using -maxdepth 5 would only apply the tests or action to the 5 deepest directories and ignore the first 5.
Find empty regular files that are 5 sub-directories deep from root:
find / -mindepth 5 -type f -empty
Using Operators with Find
In the past article "Using Control Operators" we discussed using logical AND and logical OR as control operators for Bash commands. The find command has very similar functionality built-in.
Logical AND Operator
By default two expressions will be treated as if they are joined with -a which is the logical AND operator. For example:
find ~/ -type f -iname myfile
Is the same as:
find ~/ -type f -a -iname myfile
This means find a match for a regular file (-type f) and a case insenstive match for a file named myfile (-iname myfile).
Logical OR Operator
The logical OR operator is denoted by the -o option and allows you to match exp1 or exp2. For example, let's say we wanted to match files that started with the letter s or the letter o.
$ find . -name "s" -o -name "o" ./sevenfivetwo ./ok ./ok/oktwo ./ok/oktwo/okthree ./ok/oktwo/okthree/okfour ./ok/oktwo/okthree/okfour/okfive ./ok/oktwo/okthree/okfour/okfive/oksix ./ok/oktwo/okthree/okfour/okfive/oksix/okaysixdeep ./okaydokay ./sevensevenseven
As you can see it found several files and directories that start with the letter s or the letter o.
Logical NOT Operator
The negation or invert operator is denoted by a exclamation point '!'. Just as in many other aspects of bash and Linux itself the exclamation point is a logical NOT. Basically it inverts the match, or is true if expression is false.
Let's look at an example. Let's say you were search for regular files that started with letter o.
$ find . -type f -name "o*" ./okayfile
Now, let's use the logical NOT to find regular files, EXCEPT files that start with the letter o.
$ find . -type f ! -name "o*" ./myfile
The exclamation point (sometimes called a bang) can be used on any expression. If you read it to yourself as a NOT it makes it easier to understand.
find . -type f ! -name "o*"
Find, in my current directory, files of type regular file, whose names do NOT start with the letter o.
Here is another simple example.
$ find . ! -type f . ./okaysymlink ./okdir ./mydir
Use the ! (NOT Operator) in front of -type f caused it to find all files of types that are NOT regular files. Here is found two directories and a symbolic link.
Force Precedence Operator
Wrapping expressions in parentheses sets the force precedence operator. This is useful because of the behavior of find which gives a higher precedence to -a (AND) than to -o (OR).
Please note that -a when specified implicitly (for example by two tests appearing without an explicit operator between them) or explicitly has higher precedence than -o. This means that find . -name afile -o -name bfile -print will never print afile.
- Find Man Page
The force precedence operator allows us to work within these constraints and still get the results we want. Let's take a look at an example.
I created a file and a directory starting with ok, and a file and a directory starting with my.
$ ls -l total 8 drwxrwxr-x. 2 savona savona 4096 Jul 28 17:58 mydir -rw-rw-r--. 1 savona savona 0 Jul 28 17:58 myfile -rw-rw-r--. 1 savona savona 0 Jul 28 17:57 okayfile drwxrwxr-x. 2 savona savona 4096 Jul 28 17:58 okdir
Now let's run a command looking for files of type f (regular file) that start with my OR ok.
$ find . -type f -name "my*" -o -name "ok*" ./okdir ./okayfile ./myfile
As you can see it returned a directory even though we passed the -type f option to return only regular files. This is because the two expressions implicitly using the AND operator took precedence over the expression using the OR operator. This caused the first expressions to run first finding only myfile. Then the second expression ran by itself, finding anything matching the names starting with ok.
Now let's wrap the OR expression in parentheses (force precedence operator) and see what happens.
$ find . -type f '(' -name "my*" -o -name "ok*" ')' ./okayfile ./myfile
Ahh, that's better. Now we get the expected results.
Perform Actions on Matched Files
The default action of the find command is to print the results to standard output (STDOUT). There are quite a few builtin actions in find, here we will try to touch on all of them.
Deleting Files with Find Command
The number one action that people seem to run is deleting matched files. The find command has a builtin -delete switch that will perform this function. Simply add -delete to the end of any find command to delete the files matched.
PLEASE NOTE: Using the -delete option is potentially risky. It does NOT require any interaction, it just deletes any matched files without notice. It is important that you know what the find command will return, before using the -delete option.
There is a safer way to delete files, continue reading to find out.
Listing Matched Files with Find Command
Another nifty option is -ls or list current files. Simply adding -ls to the end of a find command will list all the files found.
$ find . -type f -iname "my*" -ls 13764157 0 -rw-rw-r-- 1 savona savona 0 Jul 28 18:48 ./mydir/myfile2 13763611 4 -rw-rw-r-- 1 savona savona 499 Jul 28 18:50 ./myfile
Executing Commands on Matched Files
The -exec option is a widely used but little understood. Every time I discuss this with junior admins they are confused by the syntax. But once you understand it, you will love it.
Basically -exec allows you to execute any command on each matched file from the find command. Find places each matched file in the {} placeholder to which we can act on. Also, -exec will take arguments until it encounters a semicolon ";" which tell's find that is the end of the command. The semicolon needs to be escaped or quoted to protect them from expansion by the shell. Let's look at an example.
Let's find all regular files in our test directory and copy them into the /var/tmp/ directory.
$ find ~/test/ -type f -exec cp -v {} /var/tmp/ \; '/home/savona/test/okayfile' -> '/var/tmp/okayfile' '/home/savona/test/mydir/myfile2' -> '/var/tmp/myfile2' '/home/savona/test/myfile' -> '/var/tmp/myfile'
So what happened here? Let's explore the command and how it works.
We told find to find all regular files in the ~/test directory. As it found matches it placed their name, including full path into the {} placeholder. The -exec option ran the cp (copy) command, specifying {} as the file to move. We gave /var/tmp/ as the directory to move the file to and we ended the command with an escaped semicolon "\;" to denote the end of the command.
Execute Commands from Subdirectory Containing Matched File
Using the -execdir option is done the same as -exec. The major difference is -execdir runs the command from the subdirectory of the matched file.
Here is an example:
$ find ~/test/ -maxdepth 1 -type f -execdir cp -v {} mydir/ \; './okayfile' -> 'mydir/okayfile' './myfile' -> 'mydir/myfile'
Here we copied files from ~/test/ into ~/test/mydir/. We did not have to specify the full path of the target directory because we used -execdir which executed the command from the matched files subdirectory, which was the same location as the target directory.
Prompt for Approval Before Executing Commands
If you use the -ok option in place of -exec, find will prompt you before taking action on each file. This helps ensure there is no action taken that will cause you problems.
For example, let's say you wanted to delete some files. You could use the -ok option to ensure no unintended files are deleted.
find ~/test/ -type f -ok rm -v {} \;
Here is an example of the -ok option in real world use:
NOTE: -okdir can be used for the same functionality as -execdir.
Saving Find Command Output to a File
Obviously you can use a simple redirect to send the output of the find command to a file. But the -fprint option exists to do just that. Simply end a find command with -fprint <filename> and it will store the full name of the matched files into that file.
Here is an example:
$ find ~/test/ -type f -fprint output.txt $ cat output.txt /home/savona/test/okayfile /home/savona/test/mydir/okayfile /home/savona/test/mydir/myfile /home/savona/test/myfile
Conclusion
The Linux find command is a very powerful tool. It is also one that every SysAdmin needs to know in order to be proficient on the command line. We covered a lot of material here and I hope we did it clearly. If you have any questions feel free to sound off in the comments section below.