find: Searching Files by Name, Type, Size, and Time
find traverses a directory tree and matches files by name, type, size, modification time, permissions, and more. -exec runs a command on matches: -exec cmd {} \; runs one command per file; -exec cmd {} + batches all matches into one command invocation. Combining predicates with -and, -or, and -not builds precise queries.
Common find patterns
# Find files by name (case-insensitive: -iname)
find /var/log -name "*.log"
find /home -iname "*.jpg"
# Find by type
find /tmp -type f # regular files only
find /tmp -type d # directories only
find /tmp -type l # symlinks only
# Find by size
find /var -type f -size +100M # larger than 100MB
find /tmp -type f -size -1k # smaller than 1KB
find . -type f -size +10M -size -100M # between 10M and 100M
# Find by modification time (days)
find /home -type f -mtime -7 # modified in last 7 days
find /var/log -type f -mtime +30 # not modified in 30+ days
find . -type f -newer reference.txt # newer than reference.txt
# Find by permissions
find /var/www -type f -perm 777 # exactly 777
find /tmp -type f -perm /o+w # world-writable (any bit)
-exec: running commands on matches
# ';' variant: one command invocation per file
find /var -type f -name 'large*' -exec ls -lh {} \;
# Equivalent to: ls -lh file1; ls -lh file2; ls -lh file3...
# '+' variant: batch all matches into one invocation
find /var -type f -name 'large*' -exec ls -lh {} +
# Equivalent to: ls -lh file1 file2 file3...
{} is the placeholder for matched files. \; runs the command once per file. + passes all matches to a single command invocation (like xargs behavior). Use + when the command accepts multiple arguments — it's significantly faster for operations like chmod, chown, or rm:
# Delete all .tmp files (batch delete — one rm call)
find /tmp -name "*.tmp" -exec rm {} +
# Change ownership of all files in /var/www
find /var/www -type f -exec chown www-data:www-data {} +
# Find and compress old logs
find /var/log -name "*.log" -mtime +30 -exec gzip {} \;
find -exec {} + is faster than {} \\; because it avoids spawning a new process per file
ConceptShell'-exec cmd {} ;' forks a new process for each matched file. For 10,000 files, that's 10,000 fork+exec calls — slow. '-exec cmd {} +' passes all matches as arguments to a single invocation (limited by ARG_MAX, ~2MB on Linux). For rm, chmod, chown, and ls, '+' is orders of magnitude faster. The exception: when the command must run once per file because it can't accept multiple arguments, or when order per-file logging is needed.
Prerequisites
- Process forking
- Unix file permissions
- Shell expansion
Key Points
- -exec cmd {} \; → one process per file (slow for many files).
- -exec cmd {} + → one process for all files (fast, like xargs).
- -print0 | xargs -0: alternative to -exec for complex pipelines; handles filenames with spaces.
- find . -maxdepth 1: limit search to current directory only, no recursion.
Combining predicates
# AND (default): both conditions must match
find . -type f -name "*.go" -size +100k
# OR: either condition
find . -name "*.log" -o -name "*.txt"
# NOT: negate condition
find . -type f -not -name "*.bak"
# Grouping with parentheses (escaped)
find . \( -name "*.log" -o -name "*.txt" \) -mtime +7
# Find files owned by a specific user
find /home -user alice -type f
# Find files not matching a pattern (useful for cleanup)
find /var/log -name "*.log" -not -name "nginx*"
# Practical: find large files for disk cleanup
find / -type f -size +500M -exec ls -lh {} \; 2>/dev/null | sort -k5 -rh | head -20
# Find recently modified configs (last 24 hours)
find /etc -type f -mtime -1 2>/dev/null
You need to delete 50,000 .tmp files in /tmp. Which is faster: `find /tmp -name '*.tmp' -exec rm {} \\;` or `find /tmp -name '*.tmp' -exec rm {} +`?
easy-exec cmd {} \\; runs one command per file. -exec cmd {} + batches all files into one command.
ABoth are equally fast — rm is a fast command
Incorrect.The bottleneck isn't rm's speed — it's the overhead of fork+exec per invocation. 50,000 fork+exec calls for rm vs 1-2 calls is a significant difference.B`find /tmp -name '*.tmp' -exec rm {} +` is faster — it passes all 50,000 files to a single rm invocation instead of forking 50,000 separate rm processes
Correct!'-exec {} +' batches matched paths and passes them as arguments to rm: rm /tmp/a.tmp /tmp/b.tmp ... For 50,000 files, this means 1-2 rm invocations (limited by ARG_MAX argument length limit of ~2MB). '-exec {} ;' forks a new rm process for each of the 50,000 files — 50,000 fork+exec+exit sequences. On Linux, this difference is easily 100-1000× in wall time. When in doubt, use '+' for bulk operations. Use ';' only when the command doesn't support multiple file arguments.C`-exec rm {} \;` is safer because it processes one file at a time, avoiding partial deletions
Incorrect.rm processes all files independently regardless of batch or single mode. A failure on one file doesn't affect others in either case. Safety and speed are orthogonal here.DThe answer depends on whether /tmp is on SSD or HDD
Incorrect.Storage type affects I/O speed but not process fork overhead. The fork+exec overhead of 50,000 process spawns is CPU-bound overhead, not I/O-bound.
Hint:What is the overhead difference between forking 50,000 processes vs running 1 process with 50,000 arguments?