The xargs command reads items from standard input (stdin), delimited by blanks or newlines, and executes a command using those items as arguments. It's a powerful tool for building and executing commands dynamically, especially when dealing with long lists of files or data. xargs is essential for overcoming command-line argument length limitations and for creating efficient pipelines.
Key Capabilities:
Convert input into command arguments
Execute commands on multiple inputs efficiently
Handle arguments exceeding command-line limits
Process items in parallel for performance
Handle filenames with spaces and special characters
Build complex command pipelines
Note: xargs is part of the findutils package and is available on all Linux/Unix systems by default. It's frequently used with find, grep, and other commands that produce lists of items.
Command Syntax
xargs [OPTIONS] [COMMAND [initial-arguments]]
Common Options
Option
Description
-0, --null
Input items are terminated by null character instead of whitespace
-n NUM
Use at most NUM arguments per command line
-I REPLACE
Replace occurrences of REPLACE in initial arguments
-L NUM
Use at most NUM non-blank input lines per command
-P NUM
Run up to NUM processes at a time (parallel execution)
-p
Prompt before running commands (interactive mode)
-t
Print commands before executing them
-d DELIM
Input items are delimited by specified character
--max-procs=NUM
Synonym for -P (parallel processes)
--show-limits
Show operating system limits on command length
Detailed Examples
Example 1: Basic Usage - Delete Multiple Files
echo "file1.txt file2.txt file3.txt" | xargs rm
The most basic xargs usage: takes input and passes it as arguments to a command. This example:
Reads space-separated filenames from stdin
Passes them as arguments to rm
Equivalent to: rm file1.txt file2.txt file3.txt
This is the fundamental concept: xargs converts input lines into command arguments.
Tip: Without xargs, you'd need to type each filename manually. xargs automates this process when you have many items.
Example 2: Using with find - The Classic Combination
find . -name "*.tmp" -print0 | xargs -0 rm -f
One of the most common xargs patterns - finding and processing files:
find . -name "*.tmp" - Find all .tmp files
-print0 - Separate filenames with null character (handles spaces)
xargs -0 - Read null-terminated input
rm -f - Remove files without prompting
The -print0 and -0 combination is crucial for handling filenames with spaces, newlines, or special characters.
Warning: Without -print0 and -0, filenames with spaces will be split incorrectly. Always use these options together when dealing with filenames.
# Default: splits on whitespace and newlines
echo "a b c
d e f" | xargs echo
# Output: a b c d e f (all on one line)
# With -n 1: one argument per command
echo "a b c" | xargs -n 1 echo
# Output:
# a
# b
# c
# With -L 1: one line per command
echo "a b c
d e f" | xargs -L 1 echo
# Output:
# a b c
# d e f
Handling Special Characters
Problem: Filenames with Spaces
# WRONG - breaks on spaces
find . -name "*.txt" | xargs rm
# This fails for "my file.txt"
# CORRECT - use null terminator
find . -name "*.txt" -print0 | xargs -0 rm
# Works with any filename
Critical: Always use -print0 with find and -0 with xargs when dealing with filenames. This prevents issues with spaces, newlines, and special characters.
Quoting Issues
# Need to preserve quotes or special characters
# Use shell with proper escaping
find . -name "*.txt" -print0 | \
xargs -0 -I {} sh -c 'echo "Processing: {}"'
Performance Optimization
Performance Guidelines:
Use parallel processing (-P) for independent operations
Batch operations (-n) to reduce process overhead
Avoid shell when possible - direct commands are faster than sh -c
For very large datasets, consider GNU parallel instead
Test different -P values to find optimal parallelism
# Instead of: rm *.txt (fails with many files)
find . -name "*.txt" -print0 | xargs -0 rm
# Or with explicit batch size
find . -name "*.txt" | xargs -n 100 rm
Issue: xargs processes empty input
Problem: xargs runs command even with no input
Solution: Use -r option (GNU xargs):
# WRONG: runs once with no arguments
echo "" | xargs echo "Processing:"
# Output: Processing:
# CORRECT: doesn't run with empty input
echo "" | xargs -r echo "Processing:"
# Output: (nothing)
Issue: Command fails midway
xargs normally continues even if a command fails:
# To stop on first error, check exit status
find . -name "*.txt" -print0 | xargs -0 -n 1 sh -c 'process "$1" || exit 255' sh
# Or use set -e in shell
find . -name "*.txt" -print0 | xargs -0 sh -c 'set -e; for f; do process "$f"; done'
xargs vs. Alternatives
Method
Pros
Cons
xargs
Universal, efficient, handles argument limits
Syntax can be tricky, limited parallelism
find -exec
Simple syntax, built into find
Slower (one process per file with \;)
while read loop
Full shell control, easy to understand
Slow, creates subshells
GNU parallel
Advanced features, excellent parallelism
Not always installed by default
When to Use Each
# xargs: general purpose, good performance
find . -name "*.txt" | xargs grep "pattern"
# find -exec: simple operations, one file at a time
find . -name "*.txt" -exec cp {} /backup/ \;
# find -exec with +: batch processing (like xargs)
find . -name "*.txt" -exec rm {} +
# while read: complex logic per item
while IFS= read -r file; do
# Complex multi-line processing
do_something_complex "$file"
done < <(find . -name "*.txt")
# parallel: advanced parallel processing
parallel process ::: file1 file2 file3
Security Considerations
Security Warnings:
Shell injection: Be careful with sh -c and untrusted input
File globbing: Always quote variables and use -print0/-0
Race conditions: Files can change between find and xargs execution
Symlink attacks: Be cautious with rm and symlinks
Safe Practices
# UNSAFE: vulnerable to injection
find . -name "*.txt" | xargs -I {} sh -c "cat {}"
# SAFER: quote the argument
find . -name "*.txt" | xargs -I {} sh -c 'cat "$1"' sh {}
# SAFEST: avoid shell when possible
find . -name "*.txt" -print0 | xargs -0 cat
# For deletion, be specific
find . -type f -name "*.tmp" -print0 | xargs -0 rm -f
# Never do this without verification!
# find / | xargs rm -rf # DANGEROUS!