find: Searching Files by Name, Type, Size, and Time

1 min readSystems & Networking

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.

shellfindlinux

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?