Finding files with find
Use find to locate files by name, type, size, and age — then act on them with -delete or -exec.
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:
- Tests — filters that decide whether a file is selected.
- 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
| Flag | Matches |
|---|---|
-type f | regular files |
-type d | directories |
-type l | symbolic 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
| Goal | Tool |
|---|---|
| Locate files by name, size, age, or type | find |
| Search file contents for a pattern | grep |
| Find files containing a specific string | grep -r "pattern" . |
| Find files modified recently that contain a string | find 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.