Finding Files: find and xargs
Prerequisites: Wildcards and globbing, Pipes: connecting commands
find searches folders (recursively) for files matching conditions —
name, type, size, age — and can act on each one.
mkdir -p project/src project/logs project/docs
touch project/src/app.js project/src/util.js project/docs/guide.md
touch project/logs/a.log project/logs/b.log
find project -name "*.js" | sort
echo "---"
find project -type d | sort
echo "---"
find project -name "*.log" -o -name "*.md" | sort
project/src/app.js
project/src/util.js
---
project
project/docs
project/logs
project/src
---
project/docs/guide.md
project/logs/a.log
project/logs/b.log
| Test | Matches |
|---|---|
-name "*.js" |
name pattern (quote it!); -iname ignores case |
-type f / -type d |
files / directories |
-size +10M |
larger than 10 MB |
-mtime +7 |
modified more than 7 days ago |
-newer file |
modified more recently than file |
-maxdepth 1 |
don't go deeper than one level |
Tests are combined with AND by default; -o means OR, and ! means NOT.
Acting on results
mkdir -p logs
touch logs/a.log logs/b.log logs/keep.txt
find logs -name "*.log" -exec echo "would delete:" {} \; | sort
find logs -name "*.log" -delete
ls logs
would delete: logs/a.log
would delete: logs/b.log
keep.txt
-exec command {} \;runs a command for each file ({}is replaced with the file name).-exec command {} +passes many files at once, which is faster.-deletedeletes matches. Run the samefindwithout-deletefirst to check what it matches.
A classic cleanup job: find /var/log/myapp -name "*.log" -mtime +30 -delete.
xargs: turning lines into arguments
xargs takes a list from its input and passes it as arguments to a
command. Use -print0 and xargs -0 so names with spaces survive:
mkdir -p docs
printf 'hello\n' > "docs/read me.txt"
printf 'a\nb\nc\n' > docs/notes.txt
find docs -name "*.txt" -print0 | xargs -0 wc -l
3 docs/notes.txt
1 docs/read me.txt
4 total
-print0 separates names with a special "null" character that can't
appear in file names; xargs -0 splits on it. xargs -P 4 runs up to 4
commands in parallel — covered in Level 3.
Common mistakes
- Unquoted patterns:
find . -name *.jslets the shell expand*.jsfirst (if matching files exist in the current folder), giving confusing results. Always quote. - Piping
findinto aforloop — names with spaces break. Use-exec,-print0 | xargs -0, orwhile IFS= read -r -d '' file.