| name | xargs-parallel |
| description | Parallel execution with xargs, GNU parallel, and batch processing patterns. Use when user mentions "xargs", "parallel", "batch processing", "run in parallel", "parallel execution", "process list of files", "bulk operations", "concurrent commands", "map over files", or running commands on multiple inputs. |
xargs and Parallel Execution
xargs Basics
Read from stdin and pass as arguments to a command:
echo "file1.txt file2.txt" | xargs rm
cat urls.txt | xargs -I {} curl -O {}
echo "a b c d e f" | xargs -n 2 echo
ls *.log | xargs -t rm
xargs -a filelist.txt rm
xargs with find
Always use -print0 / -0 to handle filenames with spaces and special characters:
find . -name "*.tmp" -print0 | xargs -0 rm -f
find . -name "*.py" -print0 | xargs -0 wc -l
find /var/log -name "*.log" -print0 | xargs -0 chmod 644
find src/ -name "*.js" -print0 | xargs -0 grep -l "TODO"
Parallel Execution with xargs -P
-P N runs up to N processes in parallel:
find . -name "*.log" -print0 | xargs -0 -P 4 -I {} gzip {}
cat urls.txt | xargs -P 8 -I {} curl -sO {}
find . -name "*.png" -print0 | xargs -0 -P 4 -I {} convert {} -resize 50% resized_{}
find . -name "*.gz" -print0 | xargs -0 -P "$(nproc)" gunzip
GNU parallel Basics
GNU parallel offers more features if installed (brew install parallel / apt install parallel):
cat urls.txt | parallel curl -sO {}
find . -name "*.csv" | parallel -j 8 gzip {}
find . -name "*.mp4" | parallel --bar ffmpeg -i {} -vf scale=640:-1 small_{}
cat urls.txt | parallel --retries 3 curl -sO {}
parallel -S server1,server2 --transferfile {} gzip ::: *.log
seq 10 | parallel -k 'sleep $((RANDOM % 3)); echo {}'
Common Patterns
Bulk Rename Files
ls *.jpg | xargs -I {} mv {} archive_{}
find . -name "*.txt" -print0 | xargs -0 -I {} bash -c 'mv "$1" "${1%.txt}.md"' _ {}
ls | xargs -I {} bash -c 'mv "$1" "$(echo "$1" | tr "A-Z" "a-z")"' _ {}
Process All Files Matching a Pattern
find . -name "*.go" -print0 | xargs -0 gofmt -w
find src/ -name "*.js" -print0 | xargs -0 eslint --fix
find /etc -name "*.conf" -print0 | xargs -0 -I {} ./validate-config.sh {}
Download a List of URLs
cat urls.txt | xargs -P 10 -I {} curl -sfLO {}
cat urls.txt | xargs -P 5 -I {} wget -q --retry-connrefused --tries=3 {}
parallel --bar -j 10 curl -sfLO {} < urls.txt
Run Tests in Parallel
find tests/ -name "test_*.py" | xargs -P 4 -I {} python -m pytest {} -v
echo "unit integration e2e" | xargs -n 1 -P 3 -I {} make test-{}
Batch API Calls
find data/ -name "*.json" -print0 | xargs -0 -P 5 -I {} \
curl -s -X POST -H "Content-Type: application/json" -d @{} https://api.example.com/ingest
cat user_ids.txt | xargs -P 10 -I {} \
curl -s "https://api.example.com/users/{}" -o "responses/{}.json"
Parallel Image Compression
find . -name "*.png" -print0 | xargs -0 -P "$(nproc)" -I {} pngquant --force --quality=65-80 {} --output {}
find photos/ -name "*.jpg" -print0 | xargs -0 -P 4 -I {} \
convert {} -resize 1920x1080\> -quality 85 optimized/{}
Bulk Git Operations Across Repos
find ~/projects -maxdepth 2 -name ".git" -type d -print0 | \
xargs -0 -P 8 -I {} git -C "{}/.." pull --ff-only
find ~/projects -maxdepth 2 -name ".git" -type d | \
xargs -I {} bash -c 'echo "=== $(dirname {}) ===" && git -C "{}/.." status -s'
find ~/projects -maxdepth 2 -name ".git" -type d -print0 | \
xargs -0 -P 4 -I {} git -C "{}/.." gc --quiet
Handling Filenames with Spaces
find . -name "*.txt" -print0 | xargs -0 wc -l
ls | xargs -d '\n' -I {} echo "File: {}"
ls | tr '\n' '\0' | xargs -0 -I {} echo "File: {}"
Dry Run Before Executing
find . -name "*.bak" -print0 | xargs -0 echo rm
find . -name "*.tmp" -print0 | xargs -0 -p rm
find . -name "*.log" -print0 | xargs -0 -t gzip
Error Handling
find . -name "*.sh" -print0 | xargs -0 -P 4 bash
cat jobs.txt | parallel --halt now,fail=1 process_job {}
cat jobs.txt | parallel --halt soon,fail=20% process_job {}
cat jobs.txt | parallel --joblog joblog.txt process_job {}
xargs vs for Loops vs while read
Use xargs when:
- Processing output from
find or another command
- You want built-in parallelism (
-P)
- Batching multiple arguments per invocation (
-n)
Use while read when:
- You need complex logic per iteration (if/else, multiple commands)
- The loop body uses shell variables that must persist across iterations
Use for loops when:
- Iterating over a known, small list of items
- Glob expansion is sufficient (
for f in *.txt)
- Readability matters more than performance
for f in *.txt; do wc -l "$f"; done
find . -name "*.csv" | while read -r f; do
count=$(wc -l < "$f")
[ "$count" -gt 1000 ] && echo "Large: $f ($count lines)"
done
find . -name "*.csv" -print0 | xargs -0 -P 4 wc -l
Resource-Aware Parallelism
find . -name "*.gz" -print0 | xargs -0 -P "$(nproc)" gunzip
find . -name "*.log" -print0 | xargs -0 -P "$(( $(nproc) / 2 ))" gzip
parallel -j 50% gzip ::: *.log
parallel -j -2 gzip ::: *.log
parallel --load 80% process_job ::: *
cat urls.txt | xargs -P 5 -I {} curl -sO {}
parallel --joblog jobs.log -j 4 heavy_task ::: input_* && column -t jobs.log