Locating Files on your System with Find

The find command can be really useful once you know how to use its more powerful options. It can be use in a simple form to find files from your current directory and all the subdirectories below.

find

or to find a specific file one way it to use grep,

find | grep somefilename

A better way, and probably more efficient way, is to use find’s own name search

find -name somefilename

If you want to play along with the examples, in a tmp directory create the following files in each of the following subdirectories.

Tip Use the touch command to create the files.

Create some directories and subdirectories

cd /tmp
mkdir find_examples
cd find_examples
mkdir -p backup backup1/backup1a

Create some test files

for d in ./ backup backup1 backup1/backup1a; do touch $d/MybashScript.sh $d/mysource.c $d/MySource.c $d/Source.c; done

When you are dome it should look like this

ls -R
.:
backup	backup1  MybashScript.sh  mysource.c  MySource.c  Source.c

./backup:
MybashScript.sh  mysource.c  MySource.c  Source.c

./backup1:
backup1a  MybashScript.sh  mysource.c  MySource.c  Source.c

./backup1/backup1a:
MybashScript.sh  mysource.c  MySource.c  Source.c

Find Files Using -name

As mentioned above the simplest way to find file is to use the -name option. Using the example setup
we can search for MySource.c.

find -name MySource.c
./MySource.c
./backup/MySource.c
./backup1/MySource.c
./backup1/backup1a/MySource.c

Finding files with a pattern

If you want to use the normal shell patterns such as ‘?’, ‘*’ or even ‘[asdf]’ then you need to
put the pattern in single quotes to stop the shell from expanding them first. here is an example.

find -iname '*sh'
./backup/MybashScript.sh
./MybashScript.sh
./backup1/MybashScript.sh
./backup1/backup1a/MybashScript.sh

Find Files Using -iname but Ignoring Case

Here we use the -iname option so that find ignores the case of the name we enter.

find -iname MySource.c
./MySource.c
./mysource.c
./backup/MySource.c
./backup/mysource.c
./backup1/MySource.c
./backup1/mysource.c
./backup1/backup1a/MySource.c
./backup1/backup1a/mysource.c

Limiting the depth that find searches down a directory tree.

Using the same find as above we add the -maxdepth option and limit it to 2 subdirectories. This time we will not recurse down into the ./backup1/backup1a directory

find -maxdepth 2 -name MySource.c
./MySource.c
./backup/MySource.c
./backup1/MySource.c

Find the file between sub-directory level 2 and 4.

find -mindepth 2 -maxdepth 3 -name MySource.c
./MySource.c
./backup/MySource.c
./backup1/MySource.c

Now for the good stuff with find.

We have already seen we can find files with specific names or patterns and limit the sub-directory level we not only start at but how deep we go. Now having found the file we can do stuff with it or to it. Cool or what!

Say we want to find all the *.c files and then calculate their md5 checksums. This could be some source code for a new tool you are compiling and need to verify that what you downloaded is what the developer posted. We tell find to run or execute the command with the -exec option and we also tell find that the command we want to run passing the file name with the special token ‘{}’ and end the command that is to be run by find with ‘\;’.

find -name '*.c' -exec md5sum {} \;
d41d8cd98f00b204e9800998ecf8427e  ./mysource.c
d41d8cd98f00b204e9800998ecf8427e  ./Source.c
d41d8cd98f00b204e9800998ecf8427e  ./backup1/mysource.c
d41d8cd98f00b204e9800998ecf8427e  ./backup1/Source.c
d41d8cd98f00b204e9800998ecf8427e  ./backup1/MySource.c
d41d8cd98f00b204e9800998ecf8427e  ./backup1/backup1a/mysource.c
d41d8cd98f00b204e9800998ecf8427e  ./backup1/backup1a/Source.c
d41d8cd98f00b204e9800998ecf8427e  ./backup1/backup1a/MySource.c
d41d8cd98f00b204e9800998ecf8427e  ./backup/mysource.c
d41d8cd98f00b204e9800998ecf8427e  ./backup/Source.c
d41d8cd98f00b204e9800998ecf8427e  ./backup/MySource.c
d41d8cd98f00b204e9800998ecf8427e  ./MySource.c

Specifing names we do NOT want to match.

Using the example above but this time we want all ‘.c’ files but not those that are called Source*. That is we will find MySource.c but not Source.c.

find -name '*.c' -not -name 'Source*' -exec md5sum {} \;
d41d8cd98f00b204e9800998ecf8427e  ./mysource.c
d41d8cd98f00b204e9800998ecf8427e  ./backup1/mysource.c
d41d8cd98f00b204e9800998ecf8427e  ./backup1/MySource.c
d41d8cd98f00b204e9800998ecf8427e  ./backup1/backup1a/mysource.c
d41d8cd98f00b204e9800998ecf8427e  ./backup1/backup1a/MySource.c
d41d8cd98f00b204e9800998ecf8427e  ./backup/mysource.c
d41d8cd98f00b204e9800998ecf8427e  ./backup/MySource.c
d41d8cd98f00b204e9800998ecf8427e  ./MySource.c

See how easy it is to build up the command but adding options.

These examples can get you out of trouble.

Say “someone” has been a typical user and done something rather daft. They created a file with a strange or awkward name. Such as it has a space at the end of the file name or contains characters that are normally used as patterns (?*[]). Renaming them or deleting them can be a problem. So find to the rescue. We can use the files inode value to search with find and then use -exec to manipulate the file name as required.

We need a few of example files, we shall use the following:

testfile "testfile " 'MybashProgra?.sh'

Note that there is a space at the end of the second file name, this means that when you use ‘ls’ to see the files they appear to have the same file name. You can use touch to create the example files, put the strangely named files in quotes.

touch testfile "testfile " 'MybashProgra?.sh'

View the files with the ls command and you will see, or should see anyway :-). We use the ls command line option -1 (that’s a one) to force the utput into one column.

ls -1
testfile
testfile
MybashProgra?.sh
MybashScript.sh

You should see two file names that look the same, testfile, but you also know that cannot be true. We can use the -name and the -not -name combination to find the right file and then rename it.

find -name 'testfile*' -not -name testfile -exec mv {} old_testfile \;

Now have a look and it’s sorted out with the file name that did have the space at the end now renamed to old_testfile

ls -1
testfile
old_testfile

To rename the remaining strangely name file we will use its inode to tell find which file to rename. If we use the name itself ‘MybashProgra?.sh’ we get an error from mv saying that the destination is not a directory. That is because there are two files that match ‘MybashProgra?.sh’. To find which file we want to rename we use its inode. To find the inode of a file we use ls again as below.

ls -1i Mybash*
400494 MybashProgra?.sh
395390 MybashScript.sh

Now we can plug in the inode, 400494, to find’s -inum option and rename it as we like.

find -inum 400494 -exec mv {} new_MybashScript.sh \;
ls -1 *Mybash*
MybashScript.sh
new_MybashScript.sh

Find files based on their File type or permissions

To find files by their type use the -type option and then the type required

Common search types will be

  • d directory
  • p named pipe (FIFO)
  • f regular file
  • l symbolic link, there are some exceptions to this

So to search for the directories in our example use the following.

find  -type d
.
./backup
./backup1
./backup1/backup1a

To search and select based on permissions use the -perm option. This one gets complex rather quickly, so I’m only mentioning the basics here.

If you want to search for files that have exactly the permissions where the owner and the group have both read and write but the world only has read access use this.

find . -perm 644

It is more likely that you want to find files where the permissions are at least like those above. But you also want those that have execute permission too. The difference is that there is a ‘-‘ in front of the permissions.

find . -perm -664

You can also use the symbolic options, from chmod, to represent the permissions so the two example above would become,

find -perm  u=rw,g=rw
find -perm  -u=rw,g=rw

Finding Empty files

These files are usually lock-files and place holders created by other applications or the test files from examples on blogs explaining how to use find :-).

find -empty

Finding the big files on your system

Find even has an option to let you find files bigger or smaller than some size that you enter.

find ~ -size  128M       # Exact size match
find ~ -size +128M       # Greater than this
find ~ -size -128M       # Less than this

The ‘M’ in the examples above means Megabytes the other sizes find understands are

  • ‘b’ for 512-byte blocks (this is the default if no suffix is used)
  • ‘c’ for bytes
  • ‘w’ for two-byte words
  • ‘k’ for Kilobytes (units of 1024 bytes)
  • ‘M’ for Megabytes (units of 1048576 bytes)
  • ‘G’ for Gigabytes (units of 1073741824 bytes)

Leave a Reply

Your email address will not be published. Required fields are marked *