datarekha

Finding files with find

Use find to locate files by name, type, size, and age — then act on them with -delete or -exec.

8 min read Intermediate Command Line Lesson 6 of 14

What you'll learn

  • How find traverses a directory tree and applies tests to every file it encounters
  • The most useful tests: -name, -type, -size, -mtime, -maxdepth — and how to combine them
  • How to act on results safely with -print, -delete, and -exec

Before you start

Why find exists

grep searches inside files — it reads content and returns matching lines. find works one level up: it searches the filesystem itself, matching files and directories by their metadata — name, type, size, age, permissions — and then optionally acts on everything it matched.

The mental model is a pipeline with two stages:

  1. Tests — filters that decide whether a file is selected.
  2. Actions — what to do with each selected file.
find <path> [tests…] [action]

find walks every file under <path> recursively. Each file is run through the tests in order. If all tests pass, the action fires. The default action is -print.


Core tests

Name matching

find . -name "*.log"
./app/server.log
./tmp/debug.log

The double quotes around "*.log" are not optional. Without them the shell expands * before find ever sees the argument, matching only files in the current directory whose names end in .log — not what you want. Always quote glob patterns passed to find.

For case-insensitive matching use -iname:

find . -iname "*.JPG"

This matches photo.jpg, photo.JPG, and photo.Jpg equally.

File type

FlagMatches
-type fregular files
-type ddirectories
-type lsymbolic links

Adding -type f prevents false positives when a directory happens to be named something.log.

Size

find . -type f -size +10M

The + means more than 10 MB. Use -10M for less than 10 MB, or 10M (no prefix) for exactly 10 MB. Units: c bytes, k kilobytes, M megabytes, G gigabytes.

Modification time

find . -type f -mtime +30

-mtime +30 means the file was last modified more than 30 days ago. Use -mtime -7 for files touched within the last 7 days.

A common point of confusion: -mtime counts in 24-hour periods, rounded down. A file modified 31.5 days ago satisfies -mtime +30.

Limiting depth

find . -maxdepth 2 -name "*.log"

-maxdepth 1 restricts to the immediate directory only (no recursion). -maxdepth 2 adds one level of subdirectories. This keeps searches fast when you know where to look.


Combining tests

By default, multiple tests are joined with implicit AND — all must pass.

find . -type f -name "*.log" -size +10M -mtime +30

Use -o for OR:

find . -name "*.tmp" -o -name "*.bak"

Use ! (or -not) to negate:

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

When mixing -o with other tests, parentheses control grouping. Escape them from the shell with backslashes or wrap in quotes:

find . \( -name "*.tmp" -o -name "*.bak" \) -mtime +7

Actions

-print (default)

Prints matched paths to stdout, one per line. You get this even without writing -print, but being explicit makes intent clear.

-delete

find . -type f -name "*.log" -size +10M -mtime +30 -print
./logs/app-2025-11-01.log
./logs/app-2025-10-15.log
./tmp/crash-2025-09-30.log

Once confirmed:

find . -type f -name "*.log" -size +10M -mtime +30 -delete

Note: -delete also sets -depth implicitly, which means find processes directory contents before the directory itself — needed so that an empty directory can be removed after its contents are deleted.

-exec

-exec runs an arbitrary command on each matched file. The placeholder {} expands to the current file path. The action is terminated by \;:

find . -type f -name "*.log" -exec wc -l {} \;
      142 ./app/server.log
     1847 ./tmp/debug.log

For better performance, use + instead of \;. With +, find batches all matched paths into a single command invocation rather than one per file:

find . -type f -name "*.log" -exec wc -l {} +

Use \; when the command cannot accept multiple arguments (e.g., some scripts), or when you need each file processed independently.


Putting it all together

The original question — find every .log file bigger than 10 MB and not touched in 30 days, then delete them:

find . -type f -name "*.log" -size +10M -mtime +30 -print
./logs/api-2025-10-01.log
./logs/worker-2025-09-14.log

After reviewing the output:

find . -type f -name "*.log" -size +10M -mtime +30 -delete

find vs grep — when to use which

GoalTool
Locate files by name, size, age, or typefind
Search file contents for a patterngrep
Find files containing a specific stringgrep -r "pattern" .
Find files modified recently that contain a stringfind to narrow, then grep via -exec

They compose well:

find . -type f -name "*.log" -mtime -7 -exec grep -l "ERROR" {} +

This finds .log files modified in the last 7 days and containing the word ERROR.


Quick check

0/3
Q1You run: find . -name *.log. The shell expands *.log to server.log before find runs. What does find actually search for?
Q2Which command safely previews the files that would be deleted before committing?
Q3A teammate asks you to archive all .csv files in a data lake directory that are larger than 500 MB and older than 90 days — without touching subdirectories more than 3 levels deep. Which command fits?

Sign in to track your progress

Completed lessons, your XP, level, and streak save to your account — it's free and takes a few seconds.

Related lessons

Skip to content