The find command recursively searches directories and evaluates each file path against an expression you define.
It is one of the most important Linux tools for file discovery, cleanup tasks, and automation pipelines.
Overview
Use find when you need to:
- Locate files by name, type, size, age, owner, or permissions
- Execute actions on matched files (for example, delete, move, or run another command)
- Build reliable automation with null-safe output for paths that contain spaces
- Combine search criteria using logical operators for precise matching
Syntax
find [path...] [expression]
The command has two major parts:
path...: One or more starting directories (for example,.,/var/log,/home/user).expression: Tests, actions, options, and operators evaluated left to right.
If you omit the path, find defaults to the current directory (.).
Core Concepts
Common Tests
| Test | Purpose | Example |
|---|---|---|
-name |
Case-sensitive filename match (glob pattern) | -name "*.log" |
-iname |
Case-insensitive filename match | -iname "readme.md" |
-type |
Match file type (f, d, l, etc.) |
-type f |
-size |
Match by size (k, M, G) |
-size +100M |
-mtime |
Modified time in days | -mtime -7 |
-mmin |
Modified time in minutes | -mmin -30 |
-user |
Match file owner | -user root |
-group |
Match group owner | -group developers |
-perm |
Match permissions | -perm 644 |
Common Actions
| Action | Purpose | Example |
|---|---|---|
-print |
Print matching path (default in many cases) | -print |
-print0 |
Print null-delimited paths (safe for scripting) | -print0 |
-delete |
Delete matches | -type f -name "*.tmp" -delete |
-exec |
Run command per match | -exec rm -f {} \; |
-exec ... + |
Run command in batches | -exec rm -f {} + |
-ls |
Print long listing style output | -ls |
Logical Operators
| Operator | Meaning | Notes |
|---|---|---|
-a |
Logical AND | Default between adjacent terms |
-o |
Logical OR | Use parentheses to control precedence |
! |
Logical NOT | Negates the next test/expression |
( ... ) |
Group expression | Escape in shell: \( and \) |
Common Options
| Option | Purpose | Example |
|---|---|---|
-maxdepth N |
Limit recursion depth | -maxdepth 2 |
-mindepth N |
Skip top levels before matching | -mindepth 1 |
-xdev |
Stay on one filesystem | -xdev |
-L |
Follow symbolic links | -L |
-P |
Do not follow symbolic links (default) | -P |
-H |
Follow symlinks listed on command line | -H |
Examples
find . -type f -name "*.md"
# Find directories named .git and prune them from traversal
find . -type d -name ".git" -prune -o -type f -name "*.md" -print
# Find files modified in the last 24 hours
find /var/log -type f -mtime -1
# Find files larger than 500 MB
find /data -type f -size +500M
# Find empty files and directories
find . -empty
# Safe pipeline: null-delimited output to xargs
find . -type f -name "*.log" -print0 | xargs -0 grep -n "ERROR"
# Batch-delete temporary files safely
find . -type f -name "*.tmp" -exec rm -f {} +
# Delete empty directories (review first without -delete)
find . -type d -empty
find . -type d -empty -delete
Safe Usage Guidelines
- Start with a non-destructive preview command before using
-delete. - Prefer
-print0withxargs -0for whitespace-safe scripting. - Use
-maxdepthand-mindepthto prevent accidental broad scans. - Use
-xdevwhen scanning system paths to avoid crossing mounted filesystems. - Quote glob patterns (
"*.log") so the shell does not expand them beforefindruns.
Performance Tips
- Restrict the search path to the smallest practical subtree.
- Put cheap and highly selective tests early in expressions.
- Use pruning (
-prune) to skip large directories such as dependency caches. - Prefer
-exec ... +to reduce process creation overhead versus one command per file.
Troubleshooting
"paths must precede expression"
Cause: The shell expanded a wildcard before find parsed the expression.
Fix: Quote patterns, for example -name "*.txt".
Permission denied errors
Cause: The current user cannot read parts of the tree.
Fix: Run with elevated permissions if appropriate (sudo) or suppress noise:
find / -type f -name "*.conf" 2>/dev/null
Expression logic returns unexpected matches
Cause: -o precedence and implicit -a can be confusing.
Fix: Group logic explicitly:
find . \( -name "*.md" -o -name "*.txt" \) -type f
Notes
find behavior is provided by GNU findutils on most Linux distributions.
Run man find for full platform-specific details.