Locating files quickly on a Linux system is a fundamental skill that separates confident administrators from those constantly frustrated by their filesystems. Whether you’re hunting for configuration files buried in /etc, tracking down space-hogging logs, or searching through thousands of source files for a specific function, knowing the right tools transforms what could be hours of manual browsing into seconds of targeted searching.
This guide walks you through the essential commands for finding files on Linux—from the venerable find utility to modern alternatives like fd and ripgrep. You’ll learn not just the syntax, but when to use each tool, how to combine them effectively, and how to avoid common pitfalls that can slow your system or accidentally modify important files.
- TL;DR – Quick command reference
- Using the find Command
- Basic syntax and searching by name
- Filtering by file type
- Searching by size
- Filtering by modification and change time
- Searching by permissions
- Executing commands with -exec and -delete
- Handling filenames with spaces and special characters
- Optimising find with -prune and -fstype
- Using -printf for custom output
- Using locate and updatedb
- Modern alternatives: fd and ripgrep
- Searching file contents with grep
- Real-world practical scenarios
- Combined workflow examples
- Troubleshooting and security considerations
- Performance tips
- Further reading
TL;DR – Quick command reference
find [PATH_TO_BE_SEARCHED] -name "filename.txt" # Find by exact name
find [PATH_TO_BE_SEARCHED] -iname "*.log" # Case-insensitive pattern
locate filename # Fast database search
fd pattern [PATH_TO_BE_SEARCHED] # Modern find alternative
grep -r "search term" [PATH_TO_BE_SEARCHED] # Search file contents recursively
find [PATH_TO_BE_SEARCHED] -type f -size +100M # Files larger than 100MB
find [PATH_TO_BE_SEARCHED] -mtime -7 -name "*.tmp" -delete # Find and delete files modified in last 7 days
Using the find Command
The find command is the Swiss Army knife of file searching in Linux. Present on virtually every Unix-like system, it recursively searches directory trees based on remarkably flexible criteria. Unlike database-driven tools, find performs real-time filesystem traversal, ensuring you always see current results.
Basic syntax and searching by name
The fundamental structure follows:
find [starting-path] [test-expression] [action]
find /home/user/documents -name "report.pdf"
This searches /home/user/documents all subdirectories for files named exactly “report.pdf”. The -name test is case-sensitive, so “Report.pdf” wouldn’t match. For case-insensitive searching, use-iname:
find /var/log -iname "*.LOG"
This finds files ending in .log, .LOG, .Log, or any other case variation—particularly useful when dealing with files created on case-insensitive filesystems or by inconsistent applications. Wildcard patterns work as expected: * matches any characters,? Matches a single character, and [] matches character ranges. Remember to quote patterns to prevent shell expansion:
find ~/projects -name "test_*.py"
Filtering by file type
The -type flag restricts results to specific filesystem objects:
find /etc -type f -name "*.conf" # Regular files only
find /tmp -type d -name "cache*" # Directories only
find /dev -type l # Symbolic links
find /var -type s # Sockets
This proves invaluable when you need to distinguish between a directory called backup and a file with the same name, or when listing only symbolic links for auditing purposes.
Searching by size
The -size flag accepts various units (c=bytes, k=kilobytes, M=megabytes, G=gigabytes):
find /var/log -type f -size +100M # Larger than 100MB
find ~/downloads -type f -size -10k # Smaller than 10KB
find /tmp -type f -size 0 # Exactly 0 bytes (empty files)
Size specifications use + for “greater than”, - for “less than”, and no prefix for “exactly”. Finding oversized log files before they consume your disk space is a routine administrative task:
find /var/log -type f -size +500M -exec ls -lh {} \;
Filtering by modification and change time
Time-based searches use -mtime (modification time), -ctime (metadata change time), and -atime (access time). Values represent 24-hour periods:
find /home/user -type f -mtime -7 # Modified in last 7 days
find /tmp -type f -mtime +30 # Modified more than 30 days ago
find /var/backups -mtime 0 # Modified today (last 24hrs)
For more precise control, -newermt accepts human-readable dates:
find /var/log -newermt "2025-10-01" -type f
find ~/documents -newermt "1 week ago"
This syntax, whilst not POSIX standard, works on GNU findutils and proves tremendously convenient for incident investigation or compliance audits.
Searching by permissions
The -perm flag matches files based on permission bits. Three modes exist: exact (-perm 644), all-set (-perm -644), and any-set (-perm /644):
find /var/www -type f -perm 777 # Exactly 777 (security risk!)
find /home -type f -perm -u=w -perm -g=w # User AND group writable
find /tmp -type f -perm /o=w # World-writable (any)
Finding world-writable files represents a common security audit task:
find /var/www -type f \( -perm -002 -o -perm -020 \) -ls
You can also search by owner with -uid or -user:
find /home -type f -user oldemployee # Files owned by specific user
find /tmp -uid 1000 -mtime +7 -delete # Cleanup files from UID 1000
Executing commands with -exec and -delete
The -exec action runs commands on matched files. The {} placeholder represents the found filename, and the command must end with \; or +:
find /tmp -name "*.tmp" -mtime +7 -exec rm {} \;
Using \; runs the command once per file (slower but safer), whilst + batches multiple files into one invocation (faster but potentially problematic with argument length limits). For deletions, -delete offers a safer built-in alternative:
find /var/cache -type f -name "*.cache" -mtime +30 -delete
Critical safety tip: Always test with -ls or -print before using -delete or -exec rm:
find /var/log -name "*.log" -mtime +90 -ls # Verify first!
find /var/log -name "*.log" -mtime +90 -delete # Then delete
Handling filenames with spaces and special characters
Filenames containing spaces, newlines, or special characters can break scripts. The combination of -print0 (null-separated output) and xargs -0 handles this correctly:
find /media/backup -type f -name "*.bak" -print0 | xargs -0 rm
find ~/music -type f -name "*.mp3" -print0 | xargs -0 -I {} cp {} /backup/music/
The -I {} flag for xargs defines a placeholder, similar to -exec, but xargs batches commands more efficiently.
Optimising find with -prune and -fstype
Excluding directories improves performance and prevents unnecessary traversal:
find / -path /proc -prune -o -path /sys -prune -o -name "config.yaml" -print
The -fstype flag restricts searches to specific filesystem types, preventing find from searching network mounts or temporary filesystems:
find / -fstype ext4 -o -fstype xfs -name "large-file.bin"
Combining these techniques dramatically reduces search time when working from root directories.
Using -printf for custom output
The -printf action formats output with custom specifiers:
find /var/log -type f -printf "%s %p\n" | sort -n # Size and path, sorted
find ~/documents -name "*.pdf" -printf "%TY-%Tm-%Td %p\n" # Date and filename
Format codes include %s (size), %p (path), %T (time), %u (username), and many others—consult man find for comprehensive documentation.
Using locate and updatedb
The locate command searches a pre-built database rather than scanning the filesystem in real-time, making it dramatically faster for simple name searches:
locate sshd_config
locate -i readme.txt # Case-insensitive
locate -b "\config.yaml" # Basename only (no path matching)
On most distributions, updatedb runs nightly via cron, indexing the entire filesystem. This introduces a crucial limitation: locate cannot find files created since the last database update.
Manually update the database with:
sudo updatedb
This process can take several minutes on large filesystems. The database typically resides in /var/lib/mlocate/mlocate.db or similar locations.
When to use locate:
- Quick searches for known filenames across the entire system
- Files that don’t change frequently
- Initial reconnaissance before narrowing down with
find
Limitations:
- Results may be stale
- Doesn’t search inside files
- Limited filtering compared to
find - Security: respects user permissions but shows file existence
The performance trade-off favours locate for simple queries, but find remains essential for complex criteria, recent changes, or guaranteed current results.
Modern alternatives: fd and ripgrep
The fd command
fd is a modern, user-friendly alternative to find, written in Rust. It offers sensible defaults: case-insensitive by default, ignores .gitignore patterns, and uses intuitive syntax:
fd config.yaml # Simple search
fd -e log # All .log files
fd -H -I pattern # Include hidden (-H) and ignored (-I) files
fd '^test_.*\.py$' ~/projects # Regex patterns
Install via package managers:
sudo apt install fd-find # Debian/Ubuntu (binary named fdfind)
sudo dnf install fd-find # Fedora/CentOS/RedHat/Rocky Linux
fd excels in developer workflows where .gitignore awareness prevents searching through node_modules or build artifacts. However, find remains superior for advanced permissions searches and portable scripts.
Using ripgrep for content searching
Whilst fd finds files by name, ripgrep (rg) searches file contents at extraordinary speed:
rg "function authenticate" ~/projects # Search for function name
rg -i "error" /var/log # Case-insensitive content search
rg -t python "import requests" # Search only Python files
ripgrep automatically respects .gitignore, searches recursively, and displays results with syntax highlighting. Installation:
sudo apt install ripgrep # Debian/Ubuntu
sudo dnf install ripgrep # Fedora/CentOS/RedHat/Rocky Linux
The tool dramatically outperforms traditional grep on large codebases, making it indispensable for developers searching thousands of files.
Searching file contents with grep
When modern tools aren’t available, grep remains the standard for content searching:
grep -r "ERROR" /var/log # Recursive search
grep -ri "todo" ~/projects # Case-insensitive recursive
grep -rn "import sys" ~/code # Show line numbers
grep -rl "password" /etc # List files only (no content)
The -r flag enables recursive directory traversal, -i ignores case, -n displays line numbers, and -l lists matching files without showing the matched lines.
Combining find with grep
For more control, pipe find results to grep:
find /var/log -name "*.log" -type f -exec grep -H "authentication failure" {} \;
The -H flag forces grep to print filenames even when processing single files, crucial when using -exec. A more efficient approach uses xargs:
find ~/projects -name "*.js" -print0 | xargs -0 grep -n "console.log"
This batches multiple files per grep invocation, significantly improving performance on large result sets.
Real-world practical scenarios
Scenario 1: Find and archive large log files
Your /var/log partition is filling up. Find logs older than 30 days, larger than 50MB, compress them, and verify:
find /var/log -type f -name "*.log" -mtime +30 -size +50M -print0 | \
xargs -0 -I {} sh -c 'gzip -9 "{}" && echo "Compressed: {}"'
This finds matching logs, compresses each with maximum compression (-9), and confirms each operation.
Scenario 2: Locate a missing SSH configuration file
You’ve lost your SSH key or config file somewhere in your home directory:
find ~ -name "id_rsa*" -o -name "id_ed25519*" -o -name "ssh_config" 2>/dev/null
The 2>/dev/null redirects permission errors (from directories you can’t read) to silence. The -o operators create logical OR conditions between patterns.
Alternatively, using fd:
fd -H -I 'id_rsa|id_ed25519|ssh_config' ~
Scenario 3: Find function definition across project files
You’re debugging and need to locate where validate_user() is defined in a large Python project:
rg -t python "def validate_user" ~/projects/webapp
Or using traditional tools:
find ~/projects/webapp -name "*.py" -print0 | xargs -0 grep -n "def validate_user"
The ripgrep version completes in milliseconds, whilst the find+grep combination may take seconds on large projects—but the latter works on any system without additional installations.
Combined workflow examples
Find and change permissions
Reset permissions on all PHP files to 644, directories to 755:
find /var/www/html -type f -name "*.php" -exec chmod 644 {} +
find /var/www/html -type d -exec chmod 755 {} +
The + terminator batches files, making this significantly faster than \; on large directory trees.
Find and delete empty directories
Cleanup empty directories left behind after moving files:
find /tmp -type d -empty -delete
Add -depth to process directories depth-first, ensuring parent directories are evaluated after children:
find /var/cache -depth -type d -empty -delete
Find recently modified files and copy to backup
Backup files modified in the last 24 hours:
find ~/documents -type f -mtime 0 -print0 | \
xargs -0 -I {} cp --parents {} /backup/daily/
The --parents flag preserves directory structure in the destination.
Troubleshooting and security considerations
Permission denied errors
When searching from /, you’ll encounter directories your user cannot read. Redirect errors:
find / -name "*.conf" 2>/dev/null
Or run with elevated privileges when appropriate:
sudo find /root -name "id_rsa"
Security warning: Be cautious with sudo find combined with -exec or -delete—you can inadvertently damage system files.
Avoiding symbolic link loops
By default, find doesn’t follow symbolic links. To follow them whilst avoiding loops, use -L with caution:
find -L /var -name "*.log"
If you encounter “Filesystem loop detected” warnings, add -maxdepth to limit recursion:
find -L /var -maxdepth 5 -name "*.log"
Searching only local filesystems
Prevent searches from traversing network mounts or removable media:
find / -xdev -type f -size +1G
The -xdev flag (or -mount) restricts find to the same filesystem as the starting directory—crucial for performance and avoiding hanging on unresponsive network shares.
Performance tips
- Start searches from the most specific directory possible rather than
/to reduce traversal time. - Use
-typeearly in the expression as it’s evaluated quickly:
bash
find /var -type f -name "*.log" # Faster
find /var -name "*.log" -type f # Slower
- Prune unnecessary directories like
/proc,/sys, and/dev:
bash
find / \( -path /proc -o -path /sys -o -path /dev \) -prune -o -name "*.conf" -print
- Use
locatefor initial reconnaissance, then narrow down withfind. - Limit depth with
-maxdepthwhen you know the target location:
bash
find /etc -maxdepth 2 -name “*.conf”
- Consider
fdorripgrepfor developer workstations where performance matters and.gitignoreawareness is valuable.
Further reading
For a deeper exploration of Linux file searching techniques, consult these authoritative resources:
- GNU Findutils Manual – The definitive reference for
find,locate, andxargs: https://www.gnu.org/software/findutils/manual/html_node/index.html - The Linux Documentation Project (TLDP) – Comprehensive guides on shell scripting and command-line tools: https://tldp.org/
- Arch Linux Wiki: Core Utilities – Excellent practical examples and troubleshooting for common utilities: https://wiki.archlinux.org/title/Core_utilities
The find command’s flexibility, whilst initially daunting, becomes second nature with practice. Modern alternatives like fd and ripgrep offer compelling improvements for specific use cases, but mastering find ensures you can work effectively on any Linux system, regardless of installed tools. Start with simple name searches, gradually incorporate time and size filters, and soon you’ll be crafting complex one-liners that would have taken hours of manual searching.
Lets Talk!
If you have additional comments or questions about this article, you can share them in this section.