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

Input Handling

 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

Handling Empty Input

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

Combining with Other Tools

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

Performance Comparison

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

OptionPurpose
-n NN arguments per command
-P NRun N processes in parallel
-I {}Replace {} with argument
-0Null-delimited input
-d '\n'Custom delimiter
-pPrompt before execution
-tPrint command before running
-rDon’t run if input is empty
-L NN 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.