Linux find Command: Complete Guide with Real Examples

Tested on: Ubuntu 26.04 LTS · Debian 12 · Fedora 44 · Arch Linux — Last updated: June 2026
The find command is one of the most powerful and universally available tools in Linux. It traverses the filesystem in real time, filtering by name, type, size, modification time, permissions, ownership, and more — then optionally executes arbitrary commands on every match. Most engineers know find . -name "*.txt" and stop there. This guide covers the full surface area: combining conditions, executing actions, integrating with xargs, and the real-world patterns that eliminate tedious manual work.
- Prerequisites
- Basic Syntax
- Find by Name
- Find by Type
- Find by Size
- Find by Modification Time
- Find by Permissions and Ownership
- Combining Conditions
- Limiting Search Depth
- Executing Commands with -exec
- Find and Delete
- Find + xargs: Efficient Batch Processing
- More Real-World Recipes
- find vs locate vs fd
Prerequisites
find ships with every Linux distribution as part of GNU findutils — no installation required. The examples below assume:
- A standard bash shell
- Basic terminal familiarity (navigating directories, running commands)
- Root or
sudoaccess for system-wide searches
Optional tools referenced later: xargs (part of findutils), mlocate, and fd-find.
Basic Syntax
The general form is straightforward — path first, then conditions, then actions:
find [path...] [options] [expression]The expression is evaluated left to right. Every condition that isn't joined by an explicit -o (OR) is implicitly ANDed together. Some quick orientation examples:
# Walk the current directory tree — prints everything
find .
# Walk a specific path
find /var/log
# Search multiple paths simultaneously
find /home /tmp -name "*.log"
# Suppress "Permission denied" errors (common when searching as non-root)
find / -name "*.conf" 2>/dev/nullFind by Name
# Exact match (case-sensitive)
find . -name "config.yml"
# Case-insensitive
find . -iname "readme.md"
# Wildcard: any .log file
find /var/log -name "*.log"
# Anything starting with "error"
find . -name "error*"
# Substring match anywhere in the name
find . -name "*backup*"
# Match by extension in a project directory
find ~/projects -name "*.py"A common mistake: quoting is required around glob patterns. Without quotes the shell expands *.log before find ever sees it, giving unexpected results or a "no such file" error.
Find by Type
# Regular files only
find . -type f
# Directories only
find . -type d
# Symbolic links
find . -type l
# Block devices (useful on /dev)
find /dev -type b
# Empty files
find . -type f -empty
# Empty directories (useful before cleanup)
find . -type d -emptyFind by Size
Size suffixes: c = bytes, k = kilobytes (1024), M = megabytes, G = gigabytes. Prefix + means "more than", - means "less than", no prefix means exactly that size (rounded up to the next block).
# Files larger than 100 MB
find . -size +100M
# Files smaller than 1 KB
find . -size -1k
# Files between 1 MB and 100 MB
find . -size +1M -size -100M
# Hunt down disk hogs anywhere on the system
find / -type f -size +500M 2>/dev/null
# Find surprisingly large files in a web root
find /var/www -type f -size +50MTo find the ten largest files on the system with human-readable sizes:
find / -type f -printf "%s %pn" 2>/dev/null | sort -rn | head -10 | numfmt --to=iec --field=1Sample output:
8.1G /var/lib/docker/overlay2/abc123.../merged/large-model.bin
2.3G /home/alice/Videos/recording.mkv
1.7G /var/log/journal/abc.../system.journal
...Find by Modification Time
Three time flags, all taking an integer number of 24-hour periods by default:
-mtime— last content modification-atime— last access
-ctime — last metadata change (permissions, owner)
For minute-level precision, use -mmin, -amin, -cmin.
# Modified in the last 7 days
find . -mtime -7
# Modified more than 30 days ago (archive candidates)
find . -mtime +30
# Modified exactly 1 day ago (unusual — use -mtime -1 in practice)
find . -mtime 1
# Modified in the last 60 minutes
find . -mmin -60
# Modified more than 2 hours ago
find . -mmin +120
# Newer than a reference file — great for "what changed after this deploy?"
find . -newer /etc/passwd
# Find files modified since the last git commit
find . -newer .git/COMMIT_EDITMSG -type f -not -path "./.git/*"Find by Permissions and Ownership
Permission-based searches are critical for security audits. The -perm flag accepts octal or symbolic notation. Prefix / means "any of these bits", prefix - means "all of these bits must be set".
# World-writable files — a security concern on shared systems
find / -type f -perm -o=w 2>/dev/null
# SUID binaries — can execute as file owner (often root)
find / -perm /4000 -type f 2>/dev/null
# SGID binaries
find / -perm /2000 -type f 2>/dev/null
# Both SUID and SGID in one pass
find / -perm /6000 -type f 2>/dev/null
# Files with exact permissions 644
find . -perm 644
# Files owned by a specific user
find /home -user alice
# Files owned by a specific group
find /var/www -group www-data
# Files NOT owned by root (unexpected in /etc is suspicious)
find /etc -not -user root
# Unowned files (owner UID no longer exists)
find / -nouser 2>/dev/nullCombining Conditions
By default, all conditions are ANDed — every one must match. Use -o for OR, and parentheses (escaped for the shell) to group logic.
# AND — implicit, both must match
find . -name "*.log" -size +10M
# OR — either condition matches
find . -name "*.jpg" -o -name "*.png"
# NOT
find . -not -name "*.txt"
find . ! -name "*.txt" # equivalent shorthand
# Grouping with parentheses (must be escaped or quoted)
find . ( -name "*.jpg" -o -name "*.png" ) -size +1M
# Real-world: Python files modified this week, outside .git and __pycache__
find . -name "*.py" -mtime -7
-not -path "*/.git/*"
-not -path "*/__pycache__/*"
# Config files in /etc changed by non-root users in the last day
find /etc -name "*.conf" -mtime -1 -not -user rootLimiting Search Depth
# Current directory only — no recursion
find . -maxdepth 1 -name "*.txt"
# Up to 2 levels deep
find . -maxdepth 2 -type f
# Skip the top level, search from depth 2 to 4
find . -mindepth 2 -maxdepth 4 -name "*.conf"
# Find only top-level directories (project listing)
find /opt -mindepth 1 -maxdepth 1 -type dExecuting Commands with -exec
-exec runs a command for each match. {} is replaced by the current filename. The command must terminate with ; (one process per file) or + (batched — much faster for large result sets).
# Show detailed info for each found file
find . -name "*.log" -exec ls -lh {} ;
# Delete files older than 30 days in /tmp
find /tmp -mtime +30 -exec rm {} ;
# Compress old log files in place
find /var/log -name "*.log" -mtime +7 -exec gzip {} ;
# Change permissions on all shell scripts
find . -name "*.sh" -exec chmod +x {} ;
# Copy matching files to a backup directory
find . -name "*.conf" -exec cp {} /backup/ ;
# Using + batches all matches into one command call — faster
find . -name "*.txt" -exec wc -l {} +
# PHP malware scan — find files containing eval(
find /var/www -name "*.php" -exec grep -l "eval(" {} ;Find and Delete
-delete is faster than -exec rm because it doesn't fork a new process. Always preview with -print before committing.
# Safe preview first — see exactly what will be deleted
find /tmp -mtime +30 -print
# Then delete
find /tmp -mtime +30 -delete
# Delete temp files by pattern
find . -name "*.tmp" -delete
# Remove empty directories after a cleanup
find . -type d -empty -delete
# Remove .DS_Store files left by macOS clients on a Linux server
find /var/www -name ".DS_Store" -deleteFind + xargs: Efficient Batch Processing
xargs reads filenames from stdin and passes them to a command in batches, avoiding the per-file process overhead of -exec {} ;. Always use -print0 / -0 together to handle filenames containing spaces or newlines.
# Count lines across all Python files
find . -name "*.py" -print0 | xargs -0 wc -l
# Find config files containing a specific string
find . -name "*.conf" -print0 | xargs -0 grep -l "database"
# Safe mass delete with space-proof filenames
find . -name "*.tmp" -print0 | xargs -0 rm
# Process files in parallel — 4 workers converting images
find . -name "*.jpg" -print0 | xargs -0 -P 4 -I {} convert {} {}.webp
# Archive old logs into a single tarball
find /var/log -name "*.log" -mtime +7 -print0 |
xargs -0 tar -czf /backup/old-logs-$(date +%Y%m%d).tar.gzMore Real-World Recipes
Find All Config Files Changed This Week
find /etc -name "*.conf" -mtime -7 -exec ls -lh {} ;Find Duplicate Filenames Across Different Directories
find . -type f -printf "%fn" | sort | uniq -dFind Files With Broken Symbolic Links
find . -type l ! -exec test -e {} ; -printFind Recently Changed Files After a Deployment
# Everything touched in the last 10 minutes
find /var/www -type f -mmin -10Find World-Readable Private Keys (Security Audit)
find / -name "*.pem" -o -name "id_rsa" -o -name "*.key" 2>/dev/null |
xargs -I {} stat -c "%a %U %n" {} | grep -v "^[46]00"find vs locate vs fd
| Tool | Method | Speed | Real-time? | Notes |
|---|---|---|---|---|
find | Live filesystem scan | Moderate | Yes | Always available, full feature set |
locate | Pre-built index | Very fast | No (cron-updated) | Won't see files created since last updatedb |
fd | Live filesystem scan | Faster than find | Yes | Respects .gitignore, friendlier syntax |
# Install and use locate
sudo apt install mlocate # Ubuntu/Debian
sudo dnf install mlocate # Fedora
sudo updatedb # build/update the index
locate nginx.conf
# Install fd
sudo apt install fd-find # Ubuntu/Debian — binary is fdfind
sudo dnf install fd-find # Fedora
sudo pacman -S fd # Arch — binary is fd
# fd examples
fdfind "*.py" ~/projects # Ubuntu/Debian (use 'fd' on Arch/Fedora)
fd -t d node_modules # find node_modules directories
fd -e log --older-than 7d -x rm # delete .log files older than 7 daysUse locate when you need speed and don't care if the file was created in the last hour. Use fd when working in codebases — its .gitignore awareness eliminates noise from build artifacts. Use find for everything else: scripting, cron jobs, permission aud
