Reading — step 1 of 7
Learn
~3 min readI/O and Iteration
For batch operations on files, find + xargs is the canonical Unix pattern. Bash Cookbook treats these as core daily-use tools.
find — locate files
# Find all .txt files under current dir:
find . -name '*.txt'
# By type:
find . -type f # regular files only
find . -type d # directories only
find . -type l # symlinks
# By age:
find . -mtime -7 # modified within last 7 days
find . -mtime +30 # modified more than 30 days ago
# By size:
find . -size +100M # over 100 megabytes
find . -size -1k # under 1 kilobyte
# Combine:
find /var/log -type f -name '*.log' -mtime +30
Acting on results — -exec
find . -name '*.tmp' -delete # built-in: delete
find . -name '*.tmp' -exec rm {} \; # generic: run rm per file
find . -name '*.tmp' -exec rm {} + # batched: rm with many args (faster)
{}is replaced with each filename\;runs once per file+batches into one invocation (orders of magnitude faster on many files)
xargs — pipe into a command
When you need filenames piped in:
find . -name '*.txt' | xargs wc -l # count lines in all .txt
But filenames with SPACES break this. Use null-separation:
find . -name '*.txt' -print0 | xargs -0 wc -l
-print0— separate filenames with NUL bytes-0— xargs reads NUL-separated input
Always use -print0 + -0 for production scripts. Filenames may have spaces, newlines, anything.
xargs with parallelism
find . -name '*.jpg' -print0 | xargs -0 -n1 -P4 mogrify -resize 50%
-n1— one filename per command invocation-P4— run 4 in parallel
Great for embarrassingly-parallel work — image conversion, file compression, format conversion.
Composing find with grep
# Files containing a string:
find . -name '*.go' -exec grep -l 'TODO' {} +
# More common: just use grep -r:
grep -rl 'TODO' --include='*.go' .
For most cases, grep -r (recursive) is simpler than find + grep.
Pruning — skip directories
find . -path './node_modules' -prune -o -name '*.js' -print
The -prune skips matching paths. Useful for excluding .git, node_modules, vendor, dist, etc.
When NOT to use find
- For simple recursive listing —
ls -Ror shell globs (**/*.txtwithglobstarenabled) - For matching file CONTENT — use
grep -rdirectly - When output goes through complex pipelines — consider
fd(a modern find replacement)
Common mistakes
- Spaces in filenames —
find . | xargsis broken. Always-print0 | xargs -0. - Forgetting
-type f— find returns directories too by default. -type f for files only. -exec ... \;for many files — slow. Use+for batching.- Confusing -delete with -exec rm — both work; -delete is built-in and slightly safer (atomic, no shell escape issues).
- Wildcards UNQUOTED —
find . -name *.txtexpands the glob in the CURRENT directory before find sees it. Always quote:find . -name '*.txt'.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…