This page looks best with JavaScript enabled

Unix Fu - Find

 ·   ·  ☕ 7 min read

A needle in a haystack!

Find helps you locate files based on a variety of parameters, and even allows you to manipulate them!

Keep in mind

Not all Find implementations are created equal.
This post references the GNU version, although hopefully most of the information is generic enough to be of use in most Find implementations.

Basics

The Find command has the following structure:

find [DIR] [OPTS] [EXP]

Where DIR is the directory in which you wish to search, OPTS are search options, and EXP is an expression by which to search.

The most basic practical use might look something like this:

find . -name 'config'

Which translates to “Find anything named exactly config within cwd and its contained directories”.

This would print the path (relative to where Find is launched) for both files and directories that match the given pattern.

So for a file named config in a directory named config, it would output:

src/config
src/config/config

Not so Basics

For simplicity’s sake, I’ve grouped these options under three categories: Filters, Operators and Actions.
This is completely made up and only serves as a teaching device.

I would however recommend using them in the order they are presented.

Filters

Technically called tests, these will tell Find what ‘sort of things’ you are after.

-type

Tells Find to only consider certain type of files:

-type f -> files
-type d -> directories
-type l -> symlinks

-name / -path

As far as Find is concerned, the name of a file is simply the last portion of its path.

Say you want to find all files within a directory called something, but there are many such directories under cwd.
This simply means you are looking for files with something as a part of their paths.

find . -type f -path '*something*'

As you can see, the EXP part of the command takes in reduced regex (which is why it only matches the exact string by default).
Here, we include the wildcard * since it is evaluating the full relative path of the files (cwd/path/something/myFile and/or cwd/something/myOtherFile).

Both the -name and the -path filters have case-insensitive versions: -iname and -ipath.

-regex

Unlock the full potential of regex by using the -regex flag!

-mindepth / -maxdepth

Unless told otherwise, Find will always search recursively throughout the directory structure.
You can limit the scope of the command by setting its -mindepth and -maxdepth.

These filters take a number as parameter: 0 is the directory passed to Find (cwd in our examples so far), 1 is its direct children directories, and so on.

  • find . -maxdepth 0 -type f -name 'whoami': look for a file named whoami only within the starting directory (ignoring its child directories).

  • find . -mindepth 1 -type f -name 'whoami': look for a file named whoami in all directories under cwd except cwd itself.

Operators

Mix and match (or negate) multiple searches:

-not -> negate following pattern
-a -> 'and' following pattern
-o -> 'or' following pattern

Due to the way Find processes its filters, you might find the -o operator to not behave as you would expect.
Consider using -regex with || between the two patterns if that’s the case.

Actions

There are a couple of actions that Find can perform, but by far the most common and useful one is -exec.

-exec / -execdir

You might be tempted to use a pipe to further manipulate the output.
This can get tricky due to the way Find outputs its results.

You can use -execdir [COMMAND] "{}" \; (or -exec) at the end of your command to achieve ‘pipe like’ functionality.

1
find . -name 'removeMe' -type f -execdir rm "{}" \;

Here, the COMMAND is rm, the "{}" is whatever Find found (quoted to avoid shell expansions), and \; indicates the end of the -execdir command.
This means ‘remove all files named ‘removeMe’ from cwd downwards’.

Notes on exec

A couple of things to keep in mind:

exec vs execdir

Although most of the examples you’ll see around use -exec, this launches the command from the starting directory which is hardly safe or expected behavior.
Instead, use -execdir to run the command from the directory matching Find’s search parameters.

exec vs shell

When we say that “exec runs a given command”, what we really mean is that Find runs the exec application with the given parameters.

Exec doesn’t actually open a new process with a new shell, it runs within the shell it was executed in, sort of like a sandboxed environment.
This means that shell specific functions, aliases and scripts won’t make any sense to exec, nor will piping or redirecting outputs.

This is why you’ll commonly see something like -exec bash -c "your_cool_cmd 'params' {}"\; (or with sh -c on more minimalistic systems).

\; vs \+

You might find some examples ending with a \+ instead of the \; shown above.
Simply put: \; tells -exec to run its command once per result, while using \+ the command will run only one time taking all results from Find as a single parameter.

So \+ is more efficient but, depending on the use case, not always a good fit.
Read more about it here.

Common use cases

Remove empty directories

find . -empty -type d -execdir rm "{}" \+

Detailed results

find . -type f -name '*config*' -ls
Find all config files (lang.config, config.lang, etc.) and print their properties as such:

  6454785      4 -rw-r--r--   1 eric     eric          147 jan 24 12:56 ./tsconfig.json
  6454787      4 -rw-r--r--   1 eric     eric           41 jan 24 12:55 ./config.yml
  6427340      4 -rw-r--r--   1 eric     eric           41 jan 24 12:56 ./node-config.js

Path globs

Say you want the config files under the tests/ directory (not knowing in which subdirectory it is) but you don’t want Find to consider all the config files under src/.

find . -type f -path "./tests/*/config"
Will only output the config files somewhere within the tests/ directory.

Exclude specific path

find scr/ -name '*.py' -not -path '*/site-packages/*'
Find all files ending in .py, while discarding the ones in the site-packages/ directory.

Know the name of the file/s but not the path?
find . -name 'carmen-sandiego' -printf '%h\n'
Prints the relative path (from cwd) to the results excluding their name.

Count stuff

find src/modules/UserLogin/ -type f -execdir wc -l "{}" \+
Will count how many lines are in each file under the UserLogin module, and print out a total as a bonus!

Fancy things you can do

fancy

Clean up

You are done ‘legally’ downloading music and want to clean up the left behind crap from your Music/ directory:

1
2
find Music/ -type f  -not -iname "*.mp3" -not -iname "*.ogg" -not -iname "*.wma" -not -iname "*.m4a" -execdir rm -r "{}" \;
# This is just an example, for simple use cases prefer something like rm !(*.mp3|*.ogg|*.wma|*.m4a)

Could you be more concise with a well put-together regex?
Sure, the point is that you can achieve quite a lot without it.

Plus, the same can be done with -path, in which case the regex might get a bit hairy.

More Execdir

Result-dependent Sed

find 'lady' -type f -name 'gaga' -execdir sed -i 's:dance:Just Dance:g' "{}" \;
Replace all occurrences of dance for Just Dance in any file named exactly gaga within the lady directory.

Learn more about Sed!

Remove trailing spaces from directories

find . -name '* ' -execdir bash -c 'mv "$1" "${1%"${1##*[^[:space:]]}"}" "{}"' \;
Yep.

Redirect output

find 'a_place' -execdir bash -c 'do_something_cool_on "{}" > {}_processed' \;
Here we create a new file for each processed match reached by Find.

Pipe output

find . -mindepth 1 -maxdepth 1 -type d -execdir sh -c 'ls -1 "{}" | grep -i -q "list|downloaded"' \;

Translates to: ‘List all directories not containing a file called list or download

Support the author with