find command

The find command is one of the most useful Unix and Linux file-search tools. Its syntax can feel different from newer CLI tools because it works more like a small expression language than a simple command [options] [arguments] command.

The basic structure is:

find [where to look] [what to look for] [what to do with it]

If no search path is provided, find starts from the current directory, .. If no action is provided, it prints matching paths.

Anatomy of a find Command

Example:

find . -type f -name "*.log" -print

This means:

  • .: start in the current directory
  • -type f: match files only
  • -name "*.log": match names ending in .log
  • -print: print the matching paths

The general pattern is:

find path expression action

1. Find Files by Name

Use -name for case-sensitive matching and -iname for case-insensitive matching.

find /var/log -iname "*.log"

Always quote wildcard patterns such as "*.log" so the shell does not expand them before find receives the pattern.

2. Filter by Type

By default, find can return both files and directories. Use -type to narrow the result.

Find directories named config:

find . -type d -name "config"

Find executable files:

find . -type f -perm /a+x

Common -type values:

f: regular file
d: directory
l: symbolic link

3. Find Files by Size

Use -size with + or - to find files larger or smaller than a threshold.

Find files larger than 1 GB in the home directory:

find ~ -type f -size +1G

Common units:

k: kilobytes
M: megabytes
G: gigabytes

Examples:

find . -type f -size +100M
find . -type f -size -10k

4. Find Files by Modification Time

Use -mtime for days and -mmin for minutes.

Find files modified in the last 10 minutes:

find . -type f -mmin -10

Find files modified more than 30 days ago:

find /var/log -type f -mtime +30

Meaning:

-10: less than 10 units ago
+30: more than 30 units ago
30: exactly 30 units, according to find's time rounding rules

5. Execute Commands on Results

Use -exec to run another command against matching files.

Find .bak files and delete them in bulk:

find . -type f -name "*.bak" -exec rm {} +

How it works:

  • {} is replaced with matching file paths.
  • + groups many matching files into one command where possible.
  • \; runs the command once per file.

Prefer this form for efficiency:

find . -type f -name "*.bak" -exec rm {} +

This form is slower for large result sets:

find . -type f -name "*.bak" -exec rm {} \;

When deleting files, preview the matches first:

find . -type f -name "*.bak" -print

Then run the destructive command only after confirming the result set.

6. Skip Directories with prune

In development projects, find can waste time scanning large directories such as node_modules or .git. Use -prune to skip them.

Search for JavaScript files while skipping node_modules:

find . -path "./node_modules" -prune -o -name "*.js" -print

The logic is:

If the path is ./node_modules, skip it.
Otherwise, find files matching *.js and print them.

Skip both node_modules and .git:

find . \( -path "./node_modules" -o -path "./.git" \) -prune -o -type f -name "*.js" -print

Practical Examples

Find all Markdown files:

find . -type f -name "*.md"

Find all files modified today:

find . -type f -mtime -1

Find empty directories:

find . -type d -empty

Find files larger than 500 MB:

find . -type f -size +500M

Find and rename matching files with another command:

find . -type f -name "*.jpeg" -exec sh -c 'mv "$1" "${1%.jpeg}.jpg"' _ {} \;

Summary

The key to using find well is to think in three parts:

find [where] [match conditions] [action]

Use -type, -name, -iname, -size, and time filters to narrow the result. Use -exec for automation, but preview destructive matches with -print before deleting or modifying files.