Powerful recursive file system search utility
The find command is one of the most powerful and versatile utilities in Linux/Unix. It recursively searches directory trees for files and directories that match specified criteria, and can execute commands on the results. It's an essential tool for system administration, file management, and automation.
find [path...] [expression]
Where:
path - Starting directory (defaults to current directory if omitted)expression - Tests, actions, and options that define what to find and what to do| Option | Description | Example |
|---|---|---|
-name pattern |
File name matches pattern (case-sensitive) | -name "*.txt" |
-iname pattern |
File name matches pattern (case-insensitive) | -iname "*.TXT" |
-type c |
File type (f=file, d=directory, l=link) | -type f |
-size n |
File size (+ greater, - less, no prefix exact) | -size +100M |
-mtime n |
Modified n days ago | -mtime -7 |
-user name |
Owned by user | -user root |
-perm mode |
File permissions match | -perm 644 |
-empty |
Empty files or directories | -empty |
Finding files with specific names or patterns:
# Find all .conf files in /etc
$ find /etc -name "*.conf"
/etc/ssh/sshd_config
/etc/systemd/system.conf
/etc/rsyslog.conf
# Find specific file regardless of location
$ find /var -name "access.log"
/var/log/apache2/access.log
/var/log/nginx/access.log
# Case-insensitive search
$ find /home -iname "readme.txt"
/home/user/README.txt
/home/user/projects/ReadMe.TXT
/home/user/documents/readme.txt
Explanation: The -name option performs case-sensitive pattern matching using shell wildcards (* and ?). Use -iname for case-insensitive searches. Always quote patterns to prevent shell expansion.
Locating files based on type and size criteria:
# Find all directories in current location
$ find . -type d
./documents
./documents/projects
./downloads
# Find files larger than 100MB
$ find /home/user -type f -size +100M
/home/user/videos/movie.mp4
/home/user/downloads/ubuntu.iso
# Find files smaller than 1KB (likely empty or minimal)
$ find /tmp -type f -size -1k
/tmp/test.txt
/tmp/.hidden
# Find files exactly 0 bytes (empty files)
$ find /var/log -type f -size 0
/var/log/empty.log
# Find files between 10MB and 50MB
$ find /home -type f -size +10M -size -50M
Explanation: Size can be specified in blocks (b), kilobytes (k), megabytes (M), or gigabytes (G). The + prefix means "greater than," - means "less than," and no prefix means "exactly."
Finding files based on modification, access, or change times:
# Find files modified in last 7 days
$ find /home/user/documents -type f -mtime -7
/home/user/documents/report.pdf
/home/user/documents/notes.txt
# Find files modified exactly 30 days ago
$ find /var/log -name "*.log" -mtime 30
# Find files NOT modified in last 90 days (candidates for archival)
$ find /backup -type f -mtime +90
# Find files accessed in last 24 hours
$ find /var/www -type f -atime -1
# Find files changed (metadata) in last hour
$ find /etc -cmin -60
# Find files modified in last 2 hours
$ find /tmp -type f -mmin -120
Explanation: Time options: -mtime (modified), -atime (accessed), -ctime (changed). Units in days for -time options, minutes for -min options. Negative values mean "within last N," positive means "exactly N ago."
-mtime -1 for files modified in the last 24 hours, or -mmin -60 for the last hour. This is useful for tracking recent changes or finding log files with recent activity.
Finding files based on permissions and ownership:
# Find files with specific permissions (exact match)
$ find /var/www -type f -perm 0644
/var/www/html/index.html
/var/www/html/style.css
# Find files that are world-writable (security risk)
$ find / -type f -perm -0002 2>/dev/null
/tmp/shared_file
/var/tmp/public_writable
# Find setuid files (potential security concern)
$ find / -type f -perm -4000 -ls 2>/dev/null
/usr/bin/passwd
/usr/bin/sudo
# Find files owned by specific user
$ find /home -user john -type f
/home/john/.bashrc
/home/john/documents/file.txt
# Find files owned by specific group
$ find /var/www -group www-data
# Find files with no valid owner (orphaned)
$ find / -nouser -o -nogroup 2>/dev/null
Explanation: Permission searches can use exact matches (0644) or symbolic modes. The -perm -mode finds files with at least those permission bits set. Finding setuid/setgid files is important for security audits.
Using logical operators to create complex searches:
# Find .txt OR .log files
$ find /var/log -name "*.txt" -o -name "*.log"
# Find .conf files modified in last 7 days (AND condition - implicit)
$ find /etc -name "*.conf" -mtime -7
/etc/ssh/sshd_config
/etc/nginx/nginx.conf
# Find large log files modified recently
$ find /var/log -name "*.log" -size +50M -mtime -30
# Find files that are NOT .txt files
$ find /home/user/documents -type f ! -name "*.txt"
# Find empty directories or files
$ find /tmp \( -type f -o -type d \) -empty
# Complex: Find .sh files that are executable by owner
$ find /usr/local/bin -name "*.sh" -perm -u+x
# Find files owned by user john that are larger than 100M
$ find /home -user john -size +100M -type f
Explanation: Operators: -a or AND (implicit between tests), -o for OR, ! for NOT. Use parentheses \( \) to group conditions. Without explicit operators, conditions are ANDed together.
\( and \)
Using -exec and -execdir to perform actions on search results:
# Delete all .tmp files
$ find /tmp -name "*.tmp" -type f -exec rm {} \;
# More efficient deletion using +
$ find /tmp -name "*.tmp" -type f -exec rm {} +
# Change ownership of all .log files
$ find /var/log -name "*.log" -exec chown syslog:adm {} \;
# Change permissions on all directories
$ find /var/www -type d -exec chmod 755 {} \;
# Copy all .conf files to backup directory
$ find /etc -name "*.conf" -exec cp {} /backup/configs/ \;
# Find and display detailed info for large files
$ find /home -size +500M -exec ls -lh {} \;
# Search for text in all .log files
$ find /var/log -name "*.log" -exec grep -l "error" {} \;
# Compress old log files
$ find /var/log -name "*.log" -mtime +30 -exec gzip {} \;
Explanation: The -exec action runs a command on each found file. {} is replaced with the file name. \; ends the command and runs it once per file. Using + instead of \; passes multiple files to one command invocation (more efficient).
-exec rm. Test your find command first with -print before using destructive actions.
Locating duplicate files based on size and checksums:
# Find files with same size (potential duplicates)
$ find /home/user -type f -exec ls -l {} \; | awk '{print $5, $9}' | sort -n
# Find duplicate files by MD5 sum (more thorough)
$ find /home/user/photos -type f -exec md5sum {} \; | sort | uniq -w32 -d
# Find and list duplicate files with their paths
$ find . -type f -exec md5sum {} + | sort | uniq -w32 -d --all-repeated=separate
# Script to find and report duplicates
$ find /data -type f -printf '%s\n' | sort -n | uniq -d | while read size; do
find /data -type f -size ${size}c -exec md5sum {} \;
done | sort | uniq -w32 -d
# Find files with exactly the same name in different directories
$ find /home -type f | awk -F/ '{print $NF}' | sort | uniq -d
Explanation: Finding duplicates typically involves first grouping by size (fast), then comparing checksums (slower but accurate). MD5 or SHA checksums identify identical content even with different names.
fdupes for more sophisticated duplicate detection.
Using find for routine system administration:
# Find and delete core dumps older than 7 days
$ find /var/crash -name "core.*" -mtime +7 -delete
# Find large log files and rotate them
$ find /var/log -name "*.log" -size +100M -exec logrotate -f {} \;
# Clean up old temporary files
$ find /tmp -type f -atime +30 -user nobody -delete
# Find broken symbolic links
$ find /usr/local -type l ! -exec test -e {} \; -print
/usr/local/bin/old_script
/usr/local/lib/broken_link.so
# Remove broken symlinks
$ find /home -xtype l -delete
# Find files without valid owner (after user deletion)
$ find / -nouser -print 2>/dev/null
# Archive old log files
$ find /var/log -name "*.log" -mtime +60 -exec tar -czf archived-logs.tar.gz {} +
# Find and remove empty directories
$ find /var/tmp -type d -empty -delete
Explanation: The -delete action is safer than -exec rm as it has built-in protections. -xtype l finds broken symbolic links. Regular maintenance tasks using find can be scheduled with cron.
-print before using -delete or destructive actions. Consider using -maxdepth to limit search depth for performance.
Complex file searches with includes and excludes:
# Find all Python files except in __pycache__ directories
$ find /project -name "*.py" ! -path "*/__pycache__/*"
# Find files excluding multiple directories
$ find /home -name "*.txt" \
! -path "*/node_modules/*" \
! -path "*/.git/*" \
! -path "*/vendor/*"
# Find recently modified source files, excluding build directories
$ find /project -name "*.c" -o -name "*.h" \
! -path "*/build/*" \
! -path "*/dist/*" \
-mtime -7
# Search only within specific depth
$ find /var -maxdepth 2 -name "*.conf"
/var/cache/app.conf
/var/lib/service.conf
# Find files with multiple extensions
$ find . -type f \( -name "*.jpg" -o -name "*.png" -o -name "*.gif" \)
# Regex pattern matching
$ find /etc -regextype posix-extended -regex ".*/[a-z]+\.conf$"
# Case-insensitive regex
$ find /var/log -regextype posix-extended -iregex ".*error.*\.log$"
Explanation: Use ! -path to exclude directories. -maxdepth limits recursion depth. -regex provides more powerful pattern matching than -name. Combine multiple patterns with -o (OR).
Optimizing find for speed and efficiency:
# Limit search to filesystem boundaries (don't cross mount points)
$ find /home -xdev -name "*.log"
# Stop after finding first match
$ find /etc -name "nginx.conf" -quit
/etc/nginx/nginx.conf
# Parallel execution for better performance on large searches
$ find /data -name "*.txt" -print0 | xargs -0 -P 4 -I {} grep "pattern" {}
# Prune directories to avoid searching them
$ find /var -type d -name "cache" -prune -o -name "*.log" -print
# Use -printf for custom output formatting
$ find /home -name "*.pdf" -printf "%p\t%s\t%TY-%Tm-%Td\n"
/home/user/doc.pdf 1048576 2025-01-15
# Find with null-terminated output (safe for filenames with spaces)
$ find /data -name "*.txt" -print0 | xargs -0 wc -l
# Optimize with -O flag (optimization level 1-3)
$ find -O3 /large/directory -name "*.dat"
# Show find command performance statistics
$ time find /usr -name "python*"
# Use locate for faster searches when appropriate
$ updatedb
$ locate nginx.conf # Much faster than find for indexed searches
Explanation: -xdev prevents crossing filesystem boundaries (useful for avoiding network mounts). -prune excludes directories from search. -print0 with xargs -0 handles filenames with spaces safely. For frequently searched patterns, consider using locate instead.
locate (requires updatedb), or specialized tools like fd or ripgrep which can be significantly faster for certain use cases.
| Option | Description |
|---|---|
-name pattern |
Base of file name matches shell pattern |
-iname pattern |
Like -name but case-insensitive |
-type c |
File type: f(file), d(dir), l(link), c(char), b(block), p(pipe), s(socket) |
-size [+/-]n[cwbkMG] |
File uses n units of space |
-mtime [+/-]n |
File modified n*24 hours ago |
-atime [+/-]n |
File accessed n*24 hours ago |
-ctime [+/-]n |
File status changed n*24 hours ago |
-mmin [+/-]n |
File modified n minutes ago |
-user name |
File owned by user name or UID |
-group name |
File belongs to group name or GID |
-perm mode |
File permission bits match mode |
-empty |
File is empty and is regular file or directory |
-maxdepth n |
Descend at most n levels below start point |
-mindepth n |
Do not apply tests at levels less than n |
-exec cmd {} \; |
Execute command on each file |
-delete |
Delete found files |
-print |
Print full file name (default) |
-print0 |
Print with null separator (for xargs -0) |
-print before using destructive actions-print0 with xargs -0 for files with spaces or special characters"*.txt" not *.txt-maxdepth to limit search scope and improve performance2>/dev/null when searching system directories without root-xdev to avoid crossing filesystem boundaries-prune to exclude large directories from searchxargs or -exec {} + for efficient batch operationsConsider these faster alternatives for specific use cases: