Many commands output lists. Many commands need arguments. xargs connects them. It reads input and runs a command with that input as arguments.
Basic Usage#
1
2
3
4
5
6
7
8
9
| # Without xargs (doesn't work)
find . -name "*.txt" | rm # rm doesn't read stdin
# With xargs (works)
find . -name "*.txt" | xargs rm
# What's happening
echo "file1 file2 file3" | xargs rm
# Becomes: rm file1 file2 file3
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| # Default: split on whitespace
echo "a b c" | xargs echo
# Output: a b c
# One item per line
echo -e "a\nb\nc" | xargs echo
# Output: a b c
# Handle spaces in filenames (-0 with null delimiter)
find . -name "*.txt" -print0 | xargs -0 rm
# Treat each line as one argument
cat list.txt | xargs -d '\n' command
|
Argument Placement#
1
2
3
4
5
6
7
8
9
10
| # Default: append to end
echo "file.txt" | xargs wc -l
# Becomes: wc -l file.txt
# Custom placement with -I
echo "file.txt" | xargs -I {} cp {} {}.bak
# Becomes: cp file.txt file.txt.bak
# Multiple uses of placeholder
echo "file.txt" | xargs -I {} sh -c 'echo "Processing {}"; wc -l {}'
|
Limiting Arguments#
1
2
3
4
5
6
7
8
9
10
11
12
13
| # One argument per command execution
find . -name "*.txt" | xargs -n 1 rm
# Runs: rm file1.txt, rm file2.txt, rm file3.txt (separately)
# Two arguments per execution
echo "a b c d e f" | xargs -n 2 echo
# Output:
# a b
# c d
# e f
# Limit by size (bytes)
echo "a b c d e f" | xargs -s 10 echo
|
Parallel Execution#
1
2
3
4
5
6
7
8
| # Run 4 processes in parallel
find . -name "*.jpg" | xargs -P 4 -I {} convert {} -resize 800x600 {}.resized.jpg
# All available CPUs
find . -name "*.log" | xargs -P 0 gzip
# Combined with -n
cat urls.txt | xargs -n 1 -P 10 curl -O
|
Confirmation (-p) and Verbose (-t)#
1
2
3
4
5
| # Ask before each execution
find . -name "*.bak" | xargs -p rm
# Show command before running
find . -name "*.txt" | xargs -t wc -l
|
1
2
3
4
| # Don't run if no input
find . -name "*.missing" | xargs --no-run-if-empty rm
# Short form:
find . -name "*.missing" | xargs -r rm
|
Practical Examples#
Bulk File Operations#
1
2
3
4
5
6
7
8
9
10
11
| # Delete files matching pattern
find . -name "*.tmp" -print0 | xargs -0 rm -f
# Move files to directory
find . -name "*.jpg" -print0 | xargs -0 -I {} mv {} ./images/
# Change permissions
find . -type f -name "*.sh" | xargs chmod +x
# Compress multiple files
find . -name "*.log" -mtime +7 | xargs gzip
|
Search and Process#
1
2
3
4
5
6
7
8
| # Search in found files
find . -name "*.py" | xargs grep "import os"
# Count lines in all matching files
find . -name "*.js" | xargs wc -l
# Replace text in multiple files
find . -name "*.txt" | xargs sed -i 's/old/new/g'
|
Git Operations#
1
2
3
4
5
6
7
8
| # Add specific files
git status --short | awk '{print $2}' | xargs git add
# Remove deleted files from tracking
git ls-files --deleted | xargs git rm
# Checkout specific files
echo "file1.txt file2.txt" | xargs git checkout --
|
Download Multiple URLs#
1
2
3
4
5
6
7
8
| # Download sequentially
cat urls.txt | xargs -n 1 curl -O
# Download in parallel (10 at a time)
cat urls.txt | xargs -n 1 -P 10 curl -O
# wget version
cat urls.txt | xargs -n 1 -P 5 wget -q
|
Docker Operations#
1
2
3
4
5
6
7
8
9
10
11
| # Stop all containers
docker ps -q | xargs docker stop
# Remove all stopped containers
docker ps -aq | xargs docker rm
# Remove images by pattern
docker images | grep "none" | awk '{print $3}' | xargs docker rmi
# Pull multiple images
echo "nginx redis postgres" | xargs -n 1 docker pull
|
Process Management#
1
2
3
4
5
6
7
8
| # Kill processes by name
pgrep -f "pattern" | xargs kill
# Kill processes by name (safer)
pgrep -f "my-app" | xargs -r kill -9
# Send signal to multiple PIDs
cat pids.txt | xargs kill -HUP
|
Package Management#
1
2
3
4
5
6
7
8
| # Install multiple packages
echo "vim git curl wget" | xargs sudo apt install -y
# Uninstall packages from list
cat remove.txt | xargs sudo apt remove -y
# pip install from list
cat requirements.txt | xargs -n 1 pip install
|
With find#
1
2
3
4
5
| # Process found files
find . -name "*.md" -print0 | xargs -0 -I {} pandoc {} -o {}.html
# Archive old files
find /var/log -name "*.log" -mtime +30 -print0 | xargs -0 tar -czvf old-logs.tar.gz
|
With grep#
1
2
3
4
5
| # Files containing pattern -> process
grep -l "TODO" *.py | xargs -I {} echo "File with TODOs: {}"
# Extract matches and process
grep -oh "https://[^ ]*" urls.txt | xargs -n 1 -P 5 curl -sI | grep "HTTP"
|
With awk#
1
2
3
4
5
| # Select column and process
ps aux | awk '$3 > 50 {print $2}' | xargs kill
# Format output for xargs
cat data.csv | awk -F, '{print $1}' | xargs -I {} echo "ID: {}"
|
Error Handling#
1
2
3
4
5
6
7
8
9
| # Continue on errors
find . -name "*.txt" | xargs -I {} sh -c 'command {} || true'
# Stop on first error (default behavior)
find . -name "*.txt" | xargs command
# Exit codes
echo "a b c" | xargs false
echo $? # Non-zero
|
1
2
3
4
5
6
7
8
| # Slow: one process per file
for f in *.txt; do wc -l "$f"; done
# Faster: batch arguments
ls *.txt | xargs wc -l
# Fastest: parallel execution
ls *.txt | xargs -P 4 -n 10 wc -l
|
GNU Parallel Alternative#
For complex parallel jobs, GNU Parallel offers more features:
1
2
3
4
5
6
7
8
9
10
11
| # Same as xargs -P
cat list.txt | parallel command {}
# With progress bar
cat urls.txt | parallel --bar curl -O {}
# Retry on failure
cat list.txt | parallel --retries 3 command {}
# Keep output order
cat list.txt | parallel --keep-order process {}
|
Quick Reference#
| Option | Purpose |
|---|
-n N | N arguments per command |
-P N | Run N processes in parallel |
-I {} | Replace {} with argument |
-0 | Null-delimited input |
-d '\n' | Custom delimiter |
-p | Prompt before execution |
-t | Print command before running |
-r | Don’t run if input is empty |
-L N | N lines per command |
xargs is the missing link between commands that produce output and commands that consume arguments. Master -I {} for placement, -P for parallelism, and -0 for safety with filenames. Combined with find, grep, and awk, it handles most bulk operations elegantly.
π¬ Get the Newsletter
Weekly insights on DevOps, automation, and CLI mastery. No spam, unsubscribe anytime.