Find Mastery
Overview
find recursively searches directory trees for files matching criteria. It’s essential for system administration, security auditing, and automation.
Understanding find’s Structure
From find --help:
find [-H] [-L] [-P] [-Olevel] [-D debugopts] [path...] [expression]
| Component | Description |
|---|---|
|
Where to search. Defaults to |
|
What to find. Combines: options, tests, and actions. |
Expression Components (from man page)
| Type | Purpose | Examples |
|---|---|---|
Options |
Control search behavior (always true) |
|
Tests |
Filter files (return true/false) |
|
Actions |
Do something with matches |
|
Operators |
Combine expressions |
|
Options (-maxdepth) must come BEFORE tests (-name). This is why find -name "*.log" -maxdepth 2 fails!
|
Troubleshooting: Why find Doesn’t Find
Problem 1: Wrong Starting Directory
The most common issue - you’re searching in the wrong place.
# You're in domus-captures but searching for a file in domus-ise-linux
pwd
# /home/user/atelier/_bibliotheca/domus-captures
find . -name "linux-ad-auth-dacl*"
# Returns nothing - file doesn't exist here!
# Solution: Search from the right directory
find /home/user/atelier/_bibliotheca/domus-ise-linux -name "linux-ad-auth-dacl*"
# Found!
# Or search all bibliotheca repos
find /home/user/atelier/_bibliotheca -name "linux-ad-auth-dacl*"
Problem 2: Antora xrefs Tell You Where
When you see an xref like:
xref:ise-linux::runbooks/linux-ad-auth-dacl.adoc[Linux AD Auth dACL]
Parse it:
xref:ise-linux::runbooks/linux-ad-auth-dacl.adoc
^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| Path under pages/
|
Component name = domus-ise-linux repo
So search in:
find ~/atelier/_bibliotheca/domus-ise-linux -name "linux-ad-auth-dacl*"
Problem 3: maxdepth Too Shallow
# Won't find files buried deep
find . -maxdepth 3 -name "*.adoc"
# Antora structure is typically 7+ levels deep:
# docs/asciidoc/modules/ROOT/pages/runbooks/file.adoc
# 1 2 3 4 5 6 7
# Use -maxdepth 10 or omit it entirely
find . -name "*.adoc"
Cross-Repository Search
Search All Domus Repos
# Define search root
BIBLIOTHECA="$HOME/atelier/_bibliotheca"
# Find file across all repos
find "$BIBLIOTHECA" -name "linux-ad-auth-dacl*"
# Find all nav.adoc files
find "$BIBLIOTHECA"/domus-* -name "nav.adoc"
# Find all antora.yml files
find "$BIBLIOTHECA"/domus-* -name "antora.yml" -type f
Path Options
# Current directory
find . -name "*.txt"
# Specific directory
find /var/log -name "*.log"
# Multiple directories
find /home /tmp -name "*.conf"
# All mounted filesystems
find / -name "pattern" 2>/dev/null
Search by Name
Search by Type
# Regular files
find . -type f
# Directories
find . -type d
# Symbolic links
find . -type l
# Block devices
find . -type b
# Character devices
find . -type c
# Named pipes (FIFO)
find . -type p
# Sockets
find . -type s
Search by Time
Modification Time (-mtime, -mmin)
# Modified in last 24 hours
find . -mtime 0
# Modified more than 7 days ago
find . -mtime +7
# Modified exactly 7 days ago
find . -mtime 7
# Modified less than 7 days ago
find . -mtime -7
# Modified in last 30 minutes
find . -mmin -30
# Modified more than 60 minutes ago
find . -mmin +60
Access Time (-atime, -amin)
# Accessed in last 24 hours
find . -atime 0
# Not accessed in 90 days
find . -atime +90
Search by Size
# Exactly 1MB
find . -size 1M
# More than 100MB
find . -size +100M
# Less than 1KB
find . -size -1k
# Empty files
find . -empty
# Size units: c (bytes), k (KB), M (MB), G (GB)
find . -size +1G
Search by Permissions
Search by Owner
# By username
find . -user admin
# By UID
find . -uid 1000
# By group name
find . -group wheel
# By GID
find . -gid 100
# No owner (orphaned)
find . -nouser
# No group
find . -nogroup
Combining Conditions
AND (default)
# Both conditions must match
find . -name "*.log" -mtime -7
# Explicit AND
find . -name "*.log" -a -mtime -7
OR
# Either condition
find . -name "*.log" -o -name "*.txt"
# With grouping (IMPORTANT: use \( \))
find . \( -name "*.log" -o -name "*.txt" \) -mtime -7
Actions
Print (default)
# Default action
find . -name "*.txt"
# Explicit print
find . -name "*.txt" -print
# Null-terminated (for xargs)
find . -name "*.txt" -print0
# Custom format
find . -name "*.txt" -printf "%p %s\n"
Printf Format
| Format | Description |
|---|---|
|
Full path |
|
Filename only |
|
Directory path |
|
Size in bytes |
|
Size in KB |
|
Permissions (octal) |
|
Permissions (symbolic) |
|
Owner username |
|
Group name |
|
Modification time |
|
Access time |
|
Change time |
|
Newline |
|
Tab |
# Custom output format
find . -type f -printf "%M %u:%g %s %p\n"
# CSV output
find . -type f -printf "%p,%s,%T+\n"
Depth Control
# Maximum depth
find . -maxdepth 2 -name "*.log"
# Minimum depth
find . -mindepth 2 -name "*.log"
# Exactly at depth 3
find . -mindepth 3 -maxdepth 3 -type f
Filesystem Boundaries
# Stay on same filesystem
find / -xdev -name "*.conf"
# Don't cross mount points
find /home -mount -type f
Production Examples
Cleanup and Maintenance
# Delete old log files
find /var/log -name "*.log" -mtime +30 -delete
# Compress old logs
find /var/log -name "*.log" -mtime +7 -exec gzip {} \;
# Delete empty directories
find . -type d -empty -delete
# Delete temp files older than 24h
find /tmp -type f -mtime +1 -delete
# Remove broken symlinks
find . -xtype l -delete
Security Auditing
# World-writable files
find / -type f -perm -002 2>/dev/null
# World-writable directories (sticky bit ok)
find / -type d -perm -002 ! -perm -1000 2>/dev/null
# SUID/SGID files
find / -type f \( -perm -4000 -o -perm -2000 \) -ls 2>/dev/null
# Files with no owner
find / -nouser -o -nogroup 2>/dev/null
# Recently modified system files
find /etc -type f -mtime -1
# Hidden files in unusual places
find /var /tmp -name ".*" -type f 2>/dev/null
# Executable files in /tmp
find /tmp -type f -perm -100 2>/dev/null
Disk Space Analysis
# Large files (> 100MB)
find / -type f -size +100M -ls 2>/dev/null
# Top 10 largest files
find / -type f -printf "%s %p\n" 2>/dev/null | sort -rn | head -10
# Disk usage by directory
find . -maxdepth 1 -type d -exec du -sh {} \; | sort -rh
# Core dumps
find / -name "core" -o -name "core.*" 2>/dev/null
# Old backup files
find / -name "*.bak" -mtime +30 2>/dev/null
Code and Config Management
# Find all Python files
find . -name "*.py" -type f
# Find configs
find /etc -name "*.conf" -type f
# Search code for pattern
find . -name "*.py" -exec grep -l "TODO" {} \;
# Fix permissions on scripts
find . -name "*.sh" -exec chmod 755 {} \;
# Find files excluding directories
find . -name "*.js" -not -path "*/node_modules/*"
# Count lines of code
find . -name "*.py" -exec wc -l {} + | tail -1
Backup Operations
# Files modified since last backup
find . -newer /var/backup/last_backup -type f
# Copy recently modified
find . -mtime -1 -type f -exec cp {} /backup/ \;
# Create file list for backup
find /home -type f -printf "%p\n" > backup_list.txt
# Sync with rsync
find . -mtime -1 -type f -print0 | xargs -0 -I {} rsync -av {} /backup/
With xargs
# Basic usage
find . -name "*.log" | xargs rm
# Handle spaces in filenames
find . -name "*.log" -print0 | xargs -0 rm
# Parallel execution
find . -name "*.png" -print0 | xargs -0 -P 4 -I {} convert {} {}.jpg
# Limit arguments per command
find . -name "*.txt" -print0 | xargs -0 -n 10 echo
# Run command for each
find . -name "*.sh" | xargs -I {} chmod +x {}
Performance Tips
# Stop at first match
find . -name "target" -print -quit
# Limit depth first
find . -maxdepth 3 -name "*.log"
# Prune directories (more efficient than -not -path)
find . -path ./node_modules -prune -o -name "*.js" -print
# Use locate for known filenames (much faster)
locate "*.conf"
Quick Reference
# By name
find . -name "*.log" # Name pattern
find . -iname "*.log" # Case insensitive
# By type
find . -type f # Files only
find . -type d # Directories only
# By time
find . -mtime -7 # Modified < 7 days
find . -mtime +30 # Modified > 30 days
find . -mmin -60 # Modified < 60 min
# By size
find . -size +100M # > 100 MB
find . -size -1k # < 1 KB
find . -empty # Empty files
# By permissions
find . -perm 644 # Exact mode
find . -perm -100 # At least executable
find . -perm /002 # Any writable
# Actions
find . -name "*.log" -delete # Delete
find . -name "*.sh" -exec chmod +x {} \; # Execute each
find . -name "*.txt" -exec cat {} + # Execute batch
# Combining
find . -name "*.log" -mtime +7 # AND (default)
find . -name "*.log" -o -name "*.txt" # OR
find . ! -name "*.log" # NOT
Mental Model: How find Works
Evaluation Order
find processes each file through your expression left-to-right:
find . -type f -name "*.log" -mtime -7 -print
For each file:
1. Is it a regular file? (-type f)
└─ No? → Skip to next file (short-circuit)
└─ Yes? → Continue...
2. Does name match *.log? (-name)
└─ No? → Skip to next file
└─ Yes? → Continue...
3. Modified in last 7 days? (-mtime -7)
└─ No? → Skip
└─ Yes? → Continue...
4. Print it (-print)
Time Notation: The +/- Confusion
-mtime n Exactly n days ago (24-hour periods)
-mtime +n MORE than n days ago (older)
-mtime -n LESS than n days ago (newer)
Timeline:
<─────────── older ─────────── now
├─────┼─────┼─────┼─────┼─────┤
+4 +3 +2 +1 0 now
-mtime +3 = files in the "+4" zone (older than 3 days)
-mtime -3 = files in "0,+1,+2" zones (newer than 3 days)
-mtime 3 = files in exactly the "+3" zone
Antora-Specific Patterns
Navigate Antora Structure
# Antora directory structure
# repo/docs/asciidoc/modules/ROOT/
# ├── pages/ ← Content files
# ├── partials/ ← Reusable snippets
# ├── examples/ ← Include examples
# ├── images/ ← Images
# ├── attachments/ ← Downloadable files
# └── nav.adoc ← Navigation
# Find all page files
find ~/atelier/_bibliotheca/domus-* -path "*/pages/*.adoc" -type f
# Find all nav.adoc files
find ~/atelier/_bibliotheca/domus-* -name "nav.adoc" -path "*/modules/*"
# Find all partials
find ~/atelier/_bibliotheca/domus-* -path "*/partials/*.adoc"
# Find all example files
find ~/atelier/_bibliotheca/domus-* -path "*/examples/*" -type f
Find by Component
# Map xref component to directory
# xref:ise-linux:: → domus-ise-linux
# xref:infra-ops:: → domus-infra-ops
# xref:linux-ops:: → domus-linux-ops
# xref:netapi:: → domus-netapi-docs
# Find all runbooks across components
find ~/atelier/_bibliotheca/domus-* -path "*/runbooks/*.adoc" -type f
# Find file by xref path
# xref:ise-linux::runbooks/linux-ad-auth-dacl.adoc
find ~/atelier/_bibliotheca/domus-ise-linux -path "*runbooks/linux-ad-auth-dacl*"
Content Search Patterns
# Find files containing cross-component xrefs (potential issues)
find ~/atelier/_bibliotheca/domus-* -name "*.adoc" -exec grep -l 'xref:[a-z-]*::' {} \;
# Find files with :toc: directive (shouldn't exist in Antora)
find ~/atelier/_bibliotheca/domus-* -path "*/pages/*.adoc" -exec grep -l '^:toc:' {} \;
# Find D2 diagram source files
find ~/atelier/_bibliotheca/domus-* -name "*.d2" -type f
# Find large images (> 500KB)
find ~/atelier/_bibliotheca/domus-* -path "*/images/*" -size +500k -type f
Build Artifact Cleanup
# Find build directories
find ~/atelier/_bibliotheca/domus-* -type d -name "build"
# Find node_modules (shouldn't be committed)
find ~/atelier/_bibliotheca/domus-* -type d -name "node_modules"
# Find cache directories
find ~/atelier/_bibliotheca/domus-* -type d -name ".cache"
# Dry run: what would be cleaned
find ~/atelier/_bibliotheca/domus-* \( -name "build" -o -name "node_modules" -o -name ".cache" \) -type d -print
# Actually clean (careful!)
# find ~/atelier/_bibliotheca/domus-* \( -name "build" -o -name "node_modules" \) -type d -exec rm -rf {} +
Reading Man Pages: find(1)
The find man page is massive. Here’s how to navigate it:
# Full man page
man find
# Search within man page (press / then type)
/EXAMPLES # Jump to examples section
/-mtime # Find -mtime documentation
/-exec # Find -exec documentation
# Quick reference sections to read:
# EXPRESSIONS - How tests/actions combine
# OPERATORS - AND, OR, NOT logic
# EXAMPLES - Real-world patterns
Key man page sections:
TESTS
-name pattern Match filename (shell glob, not regex)
-iname pattern Case-insensitive name
-path pattern Match full path
-type c File type (f=file, d=dir, l=link)
-mtime n Modified n*24 hours ago
-size n[cwbkMG] File size (c=bytes, k=KB, M=MB, G=GB)
-perm mode Permission bits
-user name Owner
-newer file Newer than reference file
ACTIONS
-print Output pathname (default)
-print0 Null-terminated (for xargs -0)
-printf format Formatted output
-exec cmd {} \; Run cmd for each file
-exec cmd {} + Run cmd with multiple files (faster)
-delete Remove file
-ls Long listing format
OPERATORS
\( expr \) Grouping (escape parens!)
! expr NOT
expr -a expr AND (default between tests)
expr -o expr OR