The Linux find command searches the filesystem in real time for files and directories matching criteria you specify. Basic usage is find [directory] [criteria] — for example, find ~ -name "*.pdf" finds all PDF files in your home directory, and find /var/log -mtime -7 finds log files modified in the last 7 days. Unlike locate, which searches a database, find always reflects the current state of the filesystem and supports dozens of filtering options including name, size, type, date, permissions, and ownership.
Introduction: Your Most Precise Search Tool
Every Linux user eventually faces the same challenge: a file is somewhere on the system, but where? Maybe you downloaded something last week and cannot remember which directory it ended up in. Maybe you need to find every configuration file belonging to a specific application. Maybe you want to locate all log files older than 30 days so you can archive or delete them. Maybe you need to find every script that has world-writable permissions as part of a security audit.
For all of these tasks, Linux provides find — one of the most powerful and flexible commands in the entire Linux toolkit. Unlike graphical file search tools that rely on indexes or keyword searches, find searches the live filesystem directly, in real time, with precise filtering criteria. It finds files not just by name but by any combination of attributes: file type, size, modification date, permissions, ownership, content, and more. And crucially, it can act on the files it finds — running any command on each result automatically.
find has a reputation for complexity, and its syntax is different enough from most Linux commands to feel unfamiliar at first. But the complexity is in service of power — once you understand find‘s structure and its core options, it becomes one of those tools you reach for constantly, because it solves problems that nothing else addresses as precisely.
This article teaches find comprehensively, from basic name searches through complex multi-criteria queries to the powerful -exec option that transforms find into a batch processing engine. Every concept is illustrated with practical examples you can use immediately.
The find Command Structure
Before exploring individual options, understanding find‘s overall structure makes everything else clearer.
Basic Syntax
find [starting_point...] [expression]The starting point is the directory (or directories) where the search begins. find descends recursively through all subdirectories from this point. If you omit the starting point, find uses the current directory.
The expression is the set of criteria and actions that determine what matches and what to do with matches. Expressions consist of:
- Tests — conditions that each file must satisfy (name matches a pattern, size exceeds a threshold, etc.)
- Actions — things to do with matching files (print them, delete them, execute a command)
- Operators — logical connectors between tests and actions (AND, OR, NOT)
When no expression is given, find prints every file it encounters — equivalent to find -print.
How find Traverses the Filesystem
find starts at the specified directory and visits every file and directory within it, recursively. For each item it visits, it evaluates the expression. Items that satisfy all tests (or the overall logical expression) are matched and the specified action is taken. Items that do not satisfy the tests are silently skipped.
This traversal is depth-first by default — find explores each directory’s contents before moving to the next directory at the same level. The traversal is also thorough: find visits every item, with no index or cache. This is what makes find accurate (it always reflects the current state) but also what makes it slower than locate for simple name searches on large filesystems.
Basic Name Searches
The most common use of find is locating files by name.
Finding by Exact Name
$ find ~ -name "report.pdf"
/home/sarah/Documents/reports/report.pdfThe -name test matches files whose name exactly matches the pattern. The match is case-sensitive — report.pdf and Report.pdf are different names.
Finding with Wildcards
-name supports shell wildcards. Important: Always quote wildcard patterns to prevent the shell from expanding them before passing them to find.
Find all PDF files anywhere in your home directory:
$ find ~ -name "*.pdf"
/home/sarah/Documents/report.pdf
/home/sarah/Downloads/manual.pdf
/home/sarah/Projects/spec.pdfFind files starting with “backup”:
$ find /var -name "backup*"Find files with a specific extension pattern:
$ find ~/code -name "*.py"Find files matching a specific naming pattern (date-prefixed files):
$ find ~/logs -name "2026-02-*"Case-Insensitive Name Search
-iname works exactly like -name but matches regardless of case:
$ find ~ -iname "readme*"
/home/sarah/projects/website/README.md
/home/sarah/projects/app/readme.txt
/home/sarah/Downloads/Readme.htmlThis matches README.md, readme.txt, and Readme.html — all case variations.
Searching in Multiple Directories
Specify multiple starting points:
$ find ~/Documents ~/Downloads -name "*.pdf"Search the entire filesystem (use with caution — can be slow and generates errors for inaccessible directories):
$ sudo find / -name "nginx.conf" 2>/dev/null<br>/etc/.nginx/nginx.conf<br>The 2>/dev/null redirects error messages (like “Permission denied” for directories find cannot access) to null, keeping the output clean.
Filtering by File Type
find distinguishes between different types of filesystem objects, allowing you to limit searches to just files, just directories, or other specific types.
The -type Test
$ find ~ -type f -name "*.txt" # Regular files only
$ find ~ -type d -name "backup*" # Directories only
$ find ~ -type l # Symbolic links onlyType values:
f— regular filed— directoryl— symbolic linkc— character device (in /dev)b— block device (in /dev)p— named pipe (FIFO)s— socket
Practical Type Filtering
Find all directories named “node_modules” (to check disk space usage by JavaScript projects):
$ find ~ -type d -name "node_modules"
/home/sarah/projects/app1/node_modules
/home/sarah/projects/app2/node_modulesFind all symbolic links in /etc that might be broken:
$ find /etc -type lFind all empty files (type f combined with -empty):
$ find ~/Downloads -type f -emptyFind all empty directories:
$ find ~/projects -type d -emptySearching by Size
find can locate files based on their size, with support for common size units and comparison operators.
The -size Test
$ find ~ -size +100M # Files LARGER than 100 megabytes
$ find ~ -size -10k # Files SMALLER than 10 kilobytes
$ find ~ -size 1G # Files EXACTLY 1 gigabyte (rarely useful)Size units:
c— bytes (characters)k— kilobytes (1024 bytes)M— megabytes (1024 KB)G— gigabytes (1024 MB)
Comparison operators:
+N— greater than N-N— less than NN(no sign) — exactly N (rounded to the nearest unit)
Practical Size Searches
Find large files consuming disk space in your home directory:
$ find ~ -type f -size +500M
/home/sarah/Videos/vacation.mkv
/home/sarah/.local/share/steam/steamapps/game.pakFind all files larger than 1 GB on the entire system:
$ sudo find / -type f -size +1G 2>/dev/nullFind suspiciously small (possibly empty or corrupted) files in a directory:
$ find ~/data -type f -size -100cCombine size with type to find large log files specifically:
$ find /var/.log -type f -size +50M
/var/.log/syslog
/var/.log/kern.log.1Searching by Modification Time
Time-based searches are among the most practically useful find capabilities — finding recently changed files, locating old files for archiving, or identifying files modified during a specific window.
Time-Based Tests
-mtime N — files modified exactly N days ago (rarely useful) -mtime +N — files modified MORE than N days ago (older than N days) -mtime -N — files modified LESS than N days ago (newer than N days)
Days are measured in 24-hour periods from the current time.
$ find ~/Documents -mtime -7 # Modified in the last 7 days
$ find /var/.log -mtime +30 # Not modified in over 30 days
$ find ~/Downloads -mtime +90 # Downloads older than 90 days-mmin N — modification time in minutes (for finer resolution):
$ find /tmp -mmin -60 # Modified in the last hour
$ find ~/Desktop -mmin +30 # Not modified in the last 30 minutesAccess Time and Change Time
find supports three timestamps for each file:
-mtime — modification time: when the file’s content was last changed -atime — access time: when the file was last read -ctime — change time: when the file’s metadata (permissions, ownership) was last changed
Access time (-atime) is often unreliable because many filesystems mount with noatime to reduce disk writes. Change time (-ctime) is useful for finding files whose permissions or ownership were recently modified.
Comparing Against a Reference File
-newer file matches files modified more recently than the reference file:
$ find ~/projects -newer ~/projects/last_backup.txtThis finds all files in your projects directory that have been modified since your last backup marker file was created — exactly what you need to know what needs backing up.
Searching by Permissions
find‘s permission-based tests are powerful for security auditing and permission management.
The -perm Test
Exact permission match:
$ find ~ -perm 644 # Files with EXACTLY 644 permissions
$ find ~ -perm 755 # Files with EXACTLY 755 permissionsAt-least permissions (slash mode):
$ find ~ -perm /o+w # Files writable by others (world-writable)
$ find ~ -perm /a+x # Files executable by anyoneThe / prefix means “at least these permissions are set” — any file where at least one of the specified bits is set matches.
All-of permissions (dash mode):
$ find ~ -perm -644 # Files where owner has rw AND others have r (minimum)
$ find ~ -perm -u+x # Files executable by their ownerThe - prefix means “all of these bits must be set.”
Critical Security Searches
Find all SetUID programs (programs that run as their owner when executed):
$ find / -perm -4000 -type f 2>/dev/null
/usr/bin/passwd
/usr/bin/sudo
/usr/bin/suFind all SetGID programs:
$ find / -perm -2000 -type f 2>/dev/nullFind world-writable files (potential security concern):
$ find / -perm /o+w -type f 2>/dev/null | grep -v procFind world-writable directories:
$ find / -perm /o+w -type d 2>/dev/null | grep -v procImportant: Running these searches on a new system and keeping a baseline lets you detect if new SetUID files or world-writable locations appear later — which could indicate a compromise.
Searching by Ownership
find can locate files by their owner or associated group.
The -user and -group Tests
Find all files owned by a specific user:
$ find /home -user sarah
$ find /var/www -user www-dataFind all files belonging to a specific group:
$ find /projects -group developersFind files NOT owned by root in system directories (potential intrusion indicator):
$ find /usr/bin -not -user root -type fFind files owned by a user who no longer exists (orphaned files with numeric UID):
$ find /home -nouser-nouser matches files whose owner UID does not correspond to any user in /etc/.passwd. Similarly, -nogroup matches files with no valid group.
Combining Owner and Permission Checks
Find SetUID files not owned by root (particularly suspicious security-wise):
$ find / -perm -4000 -not -user root -type f 2>/dev/nullCombining Multiple Criteria with Logical Operators
The real power of find emerges when you combine multiple tests with logical operators.
Implicit AND
When you list multiple tests without operators, they are implicitly joined by AND — all tests must be satisfied:
$ find ~/Documents -type f -name "*.pdf" -size +1MThis finds regular files (-type f) with a .pdf extension (-name "*.pdf") that are larger than 1 MB (-size +1M). All three conditions must be true.
$ find /var/.log -type f -name "*.log" -mtime +30Log files older than 30 days — both conditions required.
Explicit OR with -o
Use -o for logical OR — either condition satisfies the match:
$ find ~ -name "*.jpg" -o -name "*.jpeg"Finds files ending in either .jpg or .jpeg.
$ find ~/projects -name "*.py" -o -name "*.js" -o -name "*.ts"Finds Python, JavaScript, or TypeScript files.
Important grouping with OR: When using -o, use parentheses to group conditions correctly (escape them in bash):
$ find ~ \( -name "*.jpg" -o -name "*.jpeg" \) -size +5MWithout the parentheses, the -size +5M test would only apply to the .jpeg condition, not to the entire OR expression. The backslash-escaped parentheses tell find to group the name conditions.
Negation with -not or !
$ find ~/Documents -not -name "*.pdf" # All files EXCEPT PDFs
$ find /tmp -not -user sarah # Files NOT owned by sarah
$ find ~ -type f ! -name "*.py" # Not Python files (! is equivalent to -not)Complex Combined Examples
Find large images (JPEG or PNG) modified in the last week:
$ find ~/Pictures \( -name "*.jpg" -o -name "*.png" \) -size +2M -mtime -7Find Python files NOT in node_modules or .git directories:
$ find ~/projects -name "*.py" -not -path "*/node_modules/*" -not -path "*/.git/*"Find configuration files modified in the last hour (after a configuration change):
$ find /etc -type f -name "*.conf" -mmin -60Controlling Depth with -maxdepth and -mindepth
By default, find recurses infinitely deep. The depth options control how many directory levels are searched.
-maxdepth: Limit Search Depth
$ find ~ -maxdepth 1 -name "*.txt"Only searches in ~ itself, not any subdirectories. Equivalent to ls ~/*.txt.
$ find ~ -maxdepth 2 -type dLists all directories within your home directory and one level deeper — useful for a quick overview of your project structure.
$ find /etc -maxdepth 1 -name "*.conf" -type fFinds configuration files directly in /etc without descending into /etc‘s subdirectories.
-mindepth: Skip Shallow Matches
$ find ~ -mindepth 2 -name "*.log"Skips log files directly in ~ and only finds them starting from subdirectories two levels deep.
Combining maxdepth and mindepth
$ find /var/.log -mindepth 1 -maxdepth 2 -name "*.log"Searches log files in /var/.log‘s immediate subdirectories only, not in /var/.log itself or deeper than one level of subdirectory.
Pruning Directories from Search
When searching large directory trees, excluding certain directories dramatically speeds up the search and reduces noise.
-prune: Skip a Directory
$ find ~ -name ".git" -prune -o -name "*.py" -printThis is one of find‘s more complex patterns. The expression says: if the file is named .git then prune it (skip it and do not descend into it), OR print the file if it matches *.py. The -o creates an OR condition — either prune or print.
A simpler alternative using -not -path:
$ find ~ -name "*.py" -not -path "*/.git/*"Common Directory Exclusions
Exclude multiple directories from a search:
$ find ~/projects \
-not -path "*/node_modules/*" \
-not -path "*/.git/*" \
-not -path "*/__pycache__/*" \
-name "*.py"The backslash-newline continuation allows long commands to span multiple lines for readability.
Taking Action with -exec and -execdir
find becomes a batch processing powerhouse through its -exec option, which runs a command on each file found.
The -exec Action
$ find [criteria] -exec command {} \;The {} placeholder is replaced by the path of each found file. The \; marks the end of the command (the semicolon must be escaped or quoted to prevent the shell from interpreting it).
Delete all .tmp files:
$ find ~/cache -name "*.tmp" -exec rm {} \;Show detailed info for large files:
$ find ~ -size +100M -exec ls -lh {} \;Copy found files to a backup directory:
$ find ~/Documents -name "*.docx" -exec cp {} ~/backup/ \;Change permissions on all shell scripts:
$ find ~/scripts -name "*.sh" -exec chmod 755 {} \;Open each found file in your text editor (interactive):
$ find ~ -name "TODO.txt" -exec nano {} \;Using + Instead of ; for Efficiency
The \; variant runs the command once per file. The + variant passes all matching files to the command at once (like xargs), which is much faster when the command can handle multiple arguments:
$ find ~/scripts -name "*.sh" -exec chmod 755 {} +This runs chmod 755 file.1.sh file.2.sh file.3.sh (all at once) rather than chmod 755 file.1.sh then chmod 755 file.2.sh etc. For commands that accept multiple files, + is significantly faster.
-execdir: Safer Execution in File’s Directory
-execdir is like -exec but runs the command from the directory containing the found file, with the filename as ./filename rather than the full path:
$ find ~/projects -name "*.py" -execdir python3 -m py_compile {} \;-execdir is safer because it avoids potential PATH manipulation attacks and ensures the command runs in the correct context for the file.
The -ok Action: Interactive Confirmation
-ok works like -exec but asks for confirmation before running the command on each file:
$ find ~/Downloads -name "*.zip" -mtime +365 -ok rm {} \;
rm ... '/home/sarah/Downloads/old_software.zip'? y
rm ... '/home/sarah/Downloads/archive.zip'? nUseful when you want to review each match before acting on it.
Using find with xargs
For complex operations or when -exec is not flexible enough, piping find output to xargs provides additional power.
Basic xargs Usage
$ find ~/Documents -name "*.pdf" | xargs ls -lhxargs reads its standard input (the list of filenames from find) and passes them as arguments to the specified command. This is equivalent to -exec ls -lh {} + but allows more complex command construction.
Handling Filenames with Spaces
Filenames with spaces break naive xargs usage because xargs splits on whitespace. The correct approach uses null delimiters:
$ find ~ -name "*.txt" -print0 | xargs -0 grep -l "TODO"-print0 (with zero) makes find separate filenames with null characters instead of newlines. -0 tells xargs to split on null characters. This handles filenames containing spaces, newlines, and other unusual characters correctly.
Always use -print0 and xargs -0 in scripts where filenames may contain spaces.
Parallel Execution with xargs
xargs -P N runs N instances of the command in parallel — dramatically faster for CPU-intensive operations:
$ find ~/images -name "*.png" -print0 | xargs -0 -P 4 -I{} convert {} -resize 800x600 {}_resized.pngThis converts PNG images in parallel using 4 simultaneous processes.
Practical find Recipes
These ready-to-use commands address common real-world scenarios.
Disk Space Management
Find the 20 largest files in your home directory:
$ find ~ -type f -printf "%s %p\n" | sort -rn | head -20 | awk '{print $1/1024/1024 "MB " $2}'Find all files larger than 100MB:
$ find ~ -type f -size +100M -exec ls -lh {} \;Find and delete empty files:
$ find ~/Downloads -type f -empty -deleteFind and delete empty directories:
$ find ~/projects -type d -empty -deleteLog File Management
Find log files older than 30 days:
$ find /var/.log -type f -name "*.log" -mtime +30Compress old log files:
$ find /var/.log -type f -name "*.log" -mtime +7 -exec gzip {} \;Find and delete old compressed logs:
$ find /var/.log -type f -name "*.gz" -mtime +90 -deleteSecurity Auditing
Find all SetUID/SetGID executables:
$ find / \( -perm -4000 -o -perm -2000 \) -type f 2>/dev/nullFind world-writable files outside /tmp:
$ find / -perm -o+w -type f -not -path "/tmp/*" -not -path "/proc/*" 2>/dev/nullFind files recently modified in sensitive directories:
$ find /etc /usr/bin /usr/sbin -mtime -1 -type f 2>/dev/nullDevelopment Workflows
Count lines of code in all Python files (excluding virtualenv and git):
$ find . -name "*.py" -not -path "./.venv/*" -not -path "./.git/*" | xargs wc -l | tail -1Find all TODO comments in source code:
$ find ~/projects -name "*.py" -exec grep -ln "TODO\|FIXME\|HACK" {} \;Find configuration files that need updating (older than the template):
$ find /etc -name "*.conf" -newer /etc/.template.conffind vs. locate: Choosing the Right Tool
| Feature | find | locate |
|---|---|---|
| Search method | Live filesystem traversal | Pre-built database |
| Speed | Slower (thorough) | Much faster |
| Accuracy | Always current | May be out of date |
| Criteria types | Name, size, date, permissions, owner, type, content | Name/path only |
| Can act on results | Yes (-exec, -delete) | No |
| Requires database update | No | sudo updatedb |
| Root required | Only for restricted dirs | No |
| Best for | Complex criteria, accurate results, acting on files | Quick name searches |
Use locate when you just need to find where a file is by name and speed matters.
Use find when you need precision, current results, multiple criteria, or want to act on the results.
find Quick Reference
| Task | Command |
|---|---|
| Find by name (case-sensitive) | find ~ -name "*.pdf" |
| Find by name (case-insensitive) | find ~ -iname "readme*" |
| Find regular files only | find ~ -type f -name "*.txt" |
| Find directories only | find ~ -type d -name "backup*" |
| Find by size (larger than) | find ~ -size +100M |
| Find recently modified | find ~ -mtime -7 |
| Find old files | find /var/log -mtime +30 |
| Find by owner | find /home -user sarah |
| Find world-writable | find ~ -perm /o+w -type f |
| Find SetUID files | find / -perm -4000 -type f |
| Exclude directory | find ~ -not -path "*/node_modules/*" |
| Limit depth | find ~ -maxdepth 2 -name "*.conf" |
| Execute command on results | find ~ -name "*.sh" -exec chmod +x {} \; |
| Delete matching files | find ~/cache -name "*.tmp" -delete |
| Handle spaces in filenames | find ~ -name "*.txt" -print0 | xargs -0 grep "TODO" |
| Find empty files | find ~ -type f -empty |
| Find empty directories | find ~ -type d -empty |
| OR condition | find ~ \( -name "*.jpg" -o -name "*.png" \) |
| NOT condition | find ~ -not -name "*.pdf" |
Conclusion: find as a File System Language
The find command is more than a search tool — it is a language for describing files by their properties and acting on what you find. Once you internalize its structure (starting point, tests, actions, operators), you can express virtually any file selection criterion and automate operations that would be tedious or impossible with graphical tools.
The key patterns to master are: searching by name with wildcards, filtering by type, combining criteria with implicit AND, using -exec to act on results, using -print0 with xargs for safe pipeline processing, and reaching for -not and parenthesized -o when you need to exclude or express alternatives.
find is one of those commands that rewards continued learning. Each new option you discover — -newer, -perm, -empty, -printf — adds a new capability to your toolkit. The command’s designers gave it extraordinary depth precisely because file management tasks are extraordinarily varied, and having one powerful, composable tool for all of them is more valuable than a dozen simpler tools with overlapping capabilities.
Master find, and the Linux filesystem becomes a queryable, programmable space rather than a static collection of paths.








