There was a period early in my Linux journey where I’d just browse directories manually trying to remember where I put a file, which is a hilariously slow way to work once you know the find command exists. Finding files by name, type, size, modification date, or even content is one of those skills that quietly saves hours over time, and it’s worth learning properly rather than just memorizing one or two commands you copy-paste forever.
In this guide, I’ll cover find, locate, and grep (for searching file contents), with practical examples for each, plus how to combine them into more powerful one-liners.
The find Command Basics
find searches a directory tree recursively based on criteria you specify.
find /path/to/search -name "filename.txt"
This searches starting at /path/to/search for any file named exactly filename.txt.
Searching by Name
Exact Match
find . -name "report.txt"
The . means “start searching from the current directory.”
Case-Insensitive Search
find . -iname "report.txt"
-iname— ignores case, so this would also matchReport.TXTorREPORT.txt.
Using Wildcards
find . -name "*.log"
This finds every file ending in .log, regardless of what comes before it. Remember to quote the pattern so Bash doesn’t try to expand the wildcard itself before passing it to find.
Searching by File Type
find . -type f -name "*.conf"
-type f— restrict results to regular files only (not directories).
find . -type d -name "logs"
-type d— restrict results to directories only.
Other type options include -type l for symbolic links.
Searching by Size
find . -type f -size +100M
-size +100M— finds files larger than 100 megabytes.
find . -type f -size -1k
-size -1k— finds files smaller than 1 kilobyte.
Size units include c (bytes), k (kilobytes), M (megabytes), and G (gigabytes).
Searching by Modification Time
find . -type f -mtime -7
-mtime -7— files modified within the last 7 days.
find . -type f -mtime +30
-mtime +30— files modified more than 30 days ago.
For more precision down to minutes:
find . -type f -mmin -60
-mmin -60— files modified within the last 60 minutes.
Searching by Permissions
find . -type f -perm 644
This finds files with exactly 644 permissions (read/write for owner, read-only for group and others).
find / -type f -perm -4000
-perm -4000— finds files with the SUID bit set, which is often used in security audits to check for potentially risky executables.
Searching by Owner
find /home -user alice
Finds all files owned by the user alice.
find / -group developers
Finds all files owned by the group developers.
Combining Multiple Conditions
You can combine conditions using logical operators.
AND (Default Behavior)
find . -type f -name "*.log" -size +10M
Multiple conditions listed together are implicitly ANDed — this finds .log files larger than 10MB.
OR
find . -name "*.log" -o -name "*.txt"
-o— matches files with either extension.
NOT
find . -type f -not -name "*.txt"
Finds all files except .txt files.
Executing Commands on Found Files
This is where find becomes genuinely powerful — you can run a command against every result.
find . -name "*.tmp" -exec rm {} \;
Breaking this down:
-exec— run the specified command on each matched file.{}— placeholder replaced with the current file path.\;— marks the end of the command (escaped so Bash doesn’t interpret the semicolon itself).
For better performance on large result sets, use + instead of \;, which batches multiple files into fewer command invocations:
find . -name "*.tmp" -exec rm {} +
A Safer Alternative: -exec With Confirmation
find . -name "*.tmp" -ok rm {} \;
-ok— works like-execbut prompts for confirmation before running the command on each file, which is a good habit when deleting things in bulk.
Searching by File Content With grep
While find locates files based on metadata (name, size, date), grep searches inside file contents.
grep -r "TODO" /path/to/project
-r— recursive search through all files and subdirectories.
Case-Insensitive Content Search
grep -ri "error" /var/log/
Showing Line Numbers
grep -rn "function loadData" src/
-n— shows the line number where each match occurs, extremely useful when tracking down code.
Combining find and grep
find . -name "*.py" -exec grep -l "import requests" {} \;
This finds every Python file that imports the requests module. -l tells grep to print only the filenames of matches, not the matching lines themselves.
Using locate for Faster Searches
find searches the filesystem in real time, which can be slow on large systems. locate instead searches a prebuilt index, making it much faster.
locate report.txt
Before using it, make sure the index is updated:
sudo updatedb
The tradeoff is that locate won’t reflect very recent file changes until the index is refreshed, whereas find always reflects the current state of the filesystem.
Real-World Use Cases
Cleaning up temporary files:
find /tmp -type f -mtime +7 -exec rm {} \;
Removes temporary files older than 7 days, a common maintenance task on servers.
Finding large files eating disk space:
find / -type f -size +500M 2>/dev/null
The 2>/dev/null suppresses “permission denied” errors from directories you can’t access, keeping output clean.
Auditing world-writable files (a security check):
find / -type f -perm -0002 2>/dev/null
This finds files writable by anyone, which is often a red flag worth investigating.
Finding recently modified configuration files after a suspected breach:
find /etc -type f -mtime -1
Automation Example: Cleanup Script
#!/bin/bash
TARGET_DIR="/var/log/myapp"
DAYS_OLD=14
echo "Searching for log files older than $DAYS_OLD days in $TARGET_DIR..."
find "$TARGET_DIR" -type f -name "*.log" -mtime +$DAYS_OLD -print -exec rm {} \;
echo "Cleanup complete."
How it works internally:
findsearches$TARGET_DIRfor.logfiles older than$DAYS_OLDdays.-printshows each matched file before deleting it, so you get a log of what was removed.-exec rm {} \;deletes each matched file individually.- This script could be scheduled with
cronto run weekly, automatically keeping log directories from growing indefinitely.
Best Practices
- Always test
findcommands with-print(or no-execat all) before adding a destructive action likerm. - Use
-inamewhen you’re not sure about the exact case of a filename. - Prefer
+over\;with-execfor better performance on large result sets. - Redirect stderr to
/dev/nullwhen searching system-wide to avoid clutter from permission errors, but don’t do this while debugging — you might miss meaningful errors. - Combine
findwithxargsfor even more flexible command execution:find . -name "*.log" | xargs rm.
Security Considerations
- Be extremely cautious with
-exec rm— always dry-run with-printfirst to confirm exactly which files will be affected. - When searching system directories for security audits (like SUID files or world-writable files), be aware that some results are legitimate and expected — don’t blindly remove permissions without understanding why they were set.
- Avoid running
findwith-execas root against untrusted directories, since crafted filenames could potentially be used to inject unexpected behavior into poorly quoted commands.
Optimization Tips
- Use
locateinstead offindfor simple filename searches on systems where the index is kept up to date — it’s dramatically faster for large filesystems. - Limit
find‘s search depth with-maxdepthwhen you know results are within a certain number of directory levels, which speeds up searches significantly on large trees:
find . -maxdepth 2 -name "*.conf"
- Narrow searches to specific directories rather than searching from
/whenever possible.
Troubleshooting Common Issues
“Permission denied” errors cluttering output: Redirect stderr with 2>/dev/null, but remember this hides real errors too, so use cautiously.
find seems to return nothing when you expect matches: Double-check your pattern is quoted ("*.log" not *.log), since an unquoted wildcard gets expanded by Bash itself before find ever sees it.
locate returns outdated results: Run sudo updatedb to refresh the index.
-exec command fails on filenames with spaces: Use {} combined with proper quoting or switch to -exec ... + instead of \;, which handles this more gracefully in most cases.
Frequently Asked Questions
What’s the difference between find and locate? find searches the filesystem live and reflects current state but can be slower. locate searches a prebuilt index, which is fast but may be slightly out of date.
Can find search file contents directly? Not on its own — combine it with grep using -exec for content-based searches.
Is find case-sensitive by default? Yes, use -iname instead of -name for case-insensitive matching.
How do I find files modified between two specific dates? Use -newer with a reference file, or combine -mtime conditions, or use find with -newermt for specific date strings (GNU find extension): find . -newermt "2026-01-01" ! -newermt "2026-02-01".
Common Mistakes to Avoid
- Forgetting to quote wildcard patterns, causing unexpected shell expansion before
findruns. - Running
-exec rmwithout testing the search criteria first. - Searching from
/without narrowing scope, resulting in extremely slow searches and pages of permission errors. - Confusing
-name(filename pattern) with-iname(case-insensitive) and getting unexpected empty results.
Summary
Finding files efficiently in Bash comes down to mastering find for metadata-based searches (name, size, type, date, permissions) and grep for content-based searches, with locate as a fast alternative for simple filename lookups. Once you’re comfortable combining conditions and using -exec safely, you can automate cleanup tasks, security audits, and everyday file management directly from the terminal.
References
- GNU Findutils Manual: https://www.gnu.org/software/findutils/manual/html_mono/find.html
- GNU Grep Manual: https://www.gnu.org/software/grep/manual/grep.html
