Find command in Linux

Introduction

The find tool searches files and directories that match a certain criteria, and executes actions on those files. Find is one of the most useful programs you can have in your Linux toolset. This article explains the foundations of the find tool so that you can use it to its full potential.

Find Command-Line Syntax

This is the basic command-line syntax of find:

The [paths] option defines one or more paths where find will start the search.  If no paths are provided, then . is assumed (current directory)

The expression describes the files that need to be selected (tests) and what to do with those files (actions). If no expression is provided, then the -print  action is used.

There are four types of expressions:

Action return true or false depending on the success or failure status returned after executing the action.

TestsReturn true or false depending on a test condition on the file, such as name mathces an expression, it is a directory or it was modified 2 days ago.
ActionsActions are expressions that have side effects, such as printing the file name to standard output, or executing an arbitrary program.
OperatorsOperators are logical operations that join together expressions.  Examples are -a for logical AND, -o for logical OR, -! for logical NOT.

If no operator provided, then -a  is assumed.

Global Options Global options configure the operation of tests and actions

 Tests

Tests are expressions that return true or false depending on a condition on the file, such as file name matches a regular expression, file type is a directory, or file was modified 2 days ago. These are the most useful tests of the find tool (for a full list, check the find manual):

-name patternFinds files names that match the specified pattern.

It uses the same pattern matching rules as the shell, so you can use *, ? and [ ] .

It strips out the preceeding directories, so -name will not match directories even if you use   / . For that, you should use  -path.

-path patternFinds files whose path match the pattern provided.

The metacharacters  *, ? and [ ]  do not treat the directory separator  / any differently. You could therefore use "./dir*dat" to match "./dira/file.dat"

All path names start with a leading ./.

-regex patternFinds files whose path matches the provided regular expression.

By default it uses the Emacs Regular Expressions. This can be changed with -regextype

-cmin nFinds files whose contents or attributes where modified exactly n minutes ago. Use -n to specify less than n  minutes ago. Use +n to specify more than n minutes ago.
-mmin nFinds files whose contents where modified exactly n  minutes ago.  As -cmin, you can also use -n and +n.
-amin nFinds files accessed exactly  n  minutes ago.  As -cmin, you can also use -n and +n.
-ctime nFinds files whose contents or attributes where modified n*24 hours ago.  Use -n to specify less than n  days ago. Use +n to specify more than n days ago.
-mtime nFinds files whose contents where modified n*24 hours ago. As -ctime, you can also use -n and +n.
-atime nFinds files accessed  n*24 hours ago. As -ctime, you can also use -n and +n
-newer fileFinds files that were modified more recently than the file provided.
-emptyFinds files or directories that are empty
-size n[bckMG]Finds files of a particular size. You can use +n and -n to indicate file sizes more than and less than. The suffix indicates the unit.

cbyte
b512-byte blocks (default)
kkilobytes (1024 bytes)
MMegabytes
GGygabytes

To find all image files bigger than 1 Megabyte

-type cFinds files of a certain type. These are the most useful values of type  c :

fregular file
ddirectory
lsymbolic link

To find all directories and files respectively:

Actions

Actions are operations on the files that the test conditions selected.  Action return true or false depending on the success or failure status returned after executing the action.

-printPrints the full name of the matching file to the standard output, followed by new line.

This is the default action if no other actions are provided.

-lsLists the current file same as ls -dils
-deleteDeletes the selected file
-exec command ;Executes the command. The string {} is replaced by the current file name. The command ends in a ;, although usually this semicolon needs to be escaped \;

find . -exec file {} \;

-pruneSkip a directory and its sub-directories.

Operators

These are the find tool operators:

( expr )Parenthesis are used to force precedence.

You will probably need to use  \(...\)  instead of (...)  if you run from the shell

! expr ! expr True if expr  is false.
expr1 -a expr2True if both expr1 and expr2 are true.

expr2 is only evaluated if expr1 is true.

-a is the default operator, so expr1 expr2 is equivalent to expr1 -a expr2 .

expr1 -o expr2True if either  expr1 or  expr2 are true.

expr2 is only evaluated if expr1 is false.

Global Options

Global options always return true and should appear after the paths:

-depthProcesses the directory files before the directory itself.

The -delete action implies -depth.

-maxdepth levelsDescends at most the number of levels.

-maxdepth 0  means only apply the action on the starting directory

-mindepth levelsIgnores all files less than the number of levels.

-mindepth 1  means process all files but the ones in the starting directory.

Basic Usage

The most basic usage of find is to list the name of all files of the current directory, and all its sub-directories:

By default, if no path is provided, then it assumes .  (current directory).  The following command is equivalent:

Also, find uses  -print  if no other action is provided. The below command is once again equivalent:

You can also use -name to match files by name.  You can use the same pattern matching expressions as the shell, such as  *, ? and [ ] :

To find only regular files, you can use the -type f  parameter:

Or the -type d  parameter is you want to file directories only:

You could also provide multiple paths to search for all their files and directories:

Finding Files By Name

The -name pattern  expression selects files based on their file name.

The pattern matching rules are the same ones used in the shell, so you can use *, ? and [ ] to match file names:

Also, it should be noted that -name only tests the file name, thus the leading directories are removed prior to evaluating the pattern expression.  This means that if you use   / to match a parent directory,  -name will never work.  You should consider using -path  instead.

Finding Files By Path

The -path pattern  expression selects files based on their full path.

You should note the metacharacters  <em>, ? and [ ]  do not treat the directory separator  / any differently. You could therefore use "./dir</em>dat" to match across directories:

You should also notice that all paths start with a leading ./.  For example, the following path will match nothing:

If you want to match the contents of dir2, you need to use the leading ./:

Finding Files By Time

The -ctime n ,  -mtime n and  -atime n operations find files that were created, modified or accessed at some point in the past.

Understanding how  -ctime ,  -mtime and -atime reference time is tricky.    -mtime n means files whose contents or attributes where modified between  n*24 hours ago and the preceeding 24 hour period.  So for example,  -mtime 0  selects files that were modified in the last 24 hours.   -mtime 1  selects files modified between 24 and 48 hours ago.   -mtime +1  selects files modified more than 48 hours ago, and  -mtime -1 selects files modified within the last 24 hours.

The picture below illustrates how  -ctime ,  -mtime and  -atime reference time:

How -mtime and -ctime reference time
How -mtime and -ctime reference time

The example below will help clarify this even further.  Imagine the time now is 3 Sep 2016 18:00, and we have four files updated at 17:00 today (file1.txt), another at the same time yesterday (file2.txt), then on 1 Sep (file3.txt), and finally on 31 Aug (file4.txt).

The table below summarises the outputs of  -mtime (similar outputs for  -ctime  and   -atime):

CommandFiles selected
find . -type f -mtime 0file1.txt
find . -type f -mtime 1file2.txt
find . -type f -mtime 2file3.txt
find . -type f -mtime 3file4.txt
find . -type f -mtime +0file2.txt

file3.txt

file4.txt

find . -type f -mtime -1file1.txt
find . -type f -mtime +1file3.txt

file4.txt

find . -type f -mtime +0 -mtime -3file2.txt

file3.txt

Executing Commands

The -exec  action is useful to execute commands with the files you find.  This is the basic usage:

-exec is able to execute any command you like.  The file that needs to be executed can be referred to with {} .  The exec command ends with the characters \;.

A common usage us -exec  is to look for files and then grep them looking for contents:

Also, it should be noted that -exec is an expression that can return either true or false depending on the result of the execution.  For example, the isdir.sh script returns 0 if the file provided is a directory, and 1 if not.

We could then use this script to print all directories.

You could also use xargs  to execute a command.  The difference is that -exec  executes one command per file, while xargs might use multiple files for each execution.

Advanced Usage: Expressions

Find is a tool with two edges.  It has a basic and commonly used syntax  to search for files – the use case of find-, but also supports a more advanced syntax that makes it a truly powerful tool in your Linux toolbox.

The three concepts below are key to understanding the find tool:

  • It iterates over all files and sub-directories
  • For each file,  it applies the expressions provided.  Expressions are evaluated left to right, and stop as soon as evaluate to false.
  • All test conditions, operations and actions are expressions. They evaluate to either true or false.

Let’s start by looking at the following basic find command:

The above command iterates over all files and sub-directories of the current directory. For each file, it first checks whether it name matches the "*.txt"  expression. If it matches, it then prints it out.

Note that expressions are evaluated left to right. If we had first used -print  and then -name "*txt" , we would have got the nasty surprise that all files were printed out. This is because we first print the file and then we filter them out.  The filtering, of course, comes too late as we have already evaluated  -print .

Logical operations are used to group expressions. For example, the below command selects all files that have either a <em>.dat or </em>.txt extension using the OR logical operator.

Sometimes, however, you will need to use parenthesis ( (  and ) ) to group expressions.  For example, the following command does not generate what you expect. Instead of printing all files with  <em>.dat or </em>.txt extensions, it just prints out the files with  *.txt extension:

This command is equivalent to the command below. Remember that the logical AND ( -a  ) is used by default if no explicit logical expression is provided:

To print out all files matching   <em>.dat or </em>.txt, you should group the two expressions inside a parenthesis. This command first correctly evaluates the condition, and then prints files matching that criteria:

Of course, you could also use the NOT operator to exclude files that meet a condition:

By realising that find is just a tool that evaluates expressions on all files, you can end up creating complex commands such as the below to execute different actions for different files:

The above command makes use of a peculiar feature of the AND and OR logical expressions:

Expression 1OperatorExpression 2
TRUE-a (AND)Always evaluated
FALSE-a (AND)Never evaluated
TRUE-o (OR)Never evaluated
FALSE-o (OR)Always evaluated

In other words, if the first expression is FALSE, the AND operator does not need to evaluate the second expression because we already know the whole expression will be FALSE.  Similarly, when the first expression is FALSE using the OR operator, we have to evaluate the second expression as it could be TRUE.

Bibliography

The following two tabs change content below.

Eduard Manas

Eduard is a senior IT consultant with over 15 years in the financial sector. He is an experienced developer in Java, C#, Python, Wordpress, Tibco EMS/RV, Oracle, Sybase and MySQL.Outside of work, he likes spending time with family, friends, and watching football.

Latest posts by Eduard Manas (see all)

Leave a Reply