Xargs and Command Argument Processing
The xargs command builds command lines from standard input. It is commonly used when one command produces a list of items and another command expects those items as command-line arguments rather than as text on standard input.
This distinction is important in Unix pipelines:
Producer xargs Command
+-------------+ +------------------+ +------------------+
| writes text | --stdout--> | reads stdin | --argv-> | receives args |
| to stdout | | builds arguments | | and executes |
+-------------+ +------------------+ +------------------+
For example, rm expects filenames as arguments:
rm file1.txt file2.txt file3.txt
If a previous command prints those filenames, xargs can turn the printed values into arguments for rm:
printf '%s\n' file1.txt file2.txt file3.txt | xargs rm
Conceptually, xargs transforms the input into a command resembling:
rm file1.txt file2.txt file3.txt
Syntax
The general form is:
command-producing-items | xargs [XARGS_OPTIONS] command [COMMAND_OPTIONS]
For example:
printf '%s\n' one two three | xargs echo
produces:
one two three
Here, printf writes three items to standard output. xargs reads them and executes a command equivalent to:
echo one two three
Standard Input Versus Command Arguments
A pipe (|) connects the standard output of one process to the standard input of another process. It does not automatically turn each line into a command-line argument.
For example:
printf '%s\n' alpha beta | cat
works because cat can read data from standard input.
By contrast, commands such as rm, mkdir, and many uses of cp need pathnames supplied as arguments:
rm alpha beta
This is where xargs is useful:
printf '%s\n' alpha beta | xargs rm
A useful mental model is:
pipe: stdout -> stdin
xargs: stdin -> argv
where argv is the argument list passed to a program.
Common Options
| Option | Description | Example |
|---|---|---|
-n N |
Pass at most N input items to each command invocation. |
xargs -n 2 echo |
-I {} |
Replace {} in the command with one input item at a time. |
xargs -I {} mv {} backup/ |
-P N |
Run up to N command invocations in parallel. |
xargs -P 4 -n 1 gzip |
-0 |
Read NUL-separated input instead of whitespace-separated input. | find . -print0 | xargs -0 rm -- |
-t |
Print each generated command before executing it. | xargs -t echo |
-r |
On GNU xargs, do not run the command when input is empty. |
xargs -r rm -- |
The -r option is provided by GNU xargs and is not specified by POSIX, so scripts intended for multiple Unix systems should not assume it is available.
Controlling How Many Arguments Are Passed
By default, xargs tries to place as many input items as practical into each command invocation, up to system command-line size limits.
Suppose the input is:
printf '%s\n' one two three four
Using -n 2 runs echo with two arguments at a time:
printf '%s\n' one two three four | xargs -n 2 echo
Output:
one two
three four
This is useful when a command should process only a fixed number of arguments per invocation.
Replacement with -I
The -I option defines a replacement token. A common convention is {}:
printf '%s\n' server1 server2 server3 | xargs -I {} echo "checking {}"
Output:
checking server1
checking server2
checking server3
Each input item causes a separate command invocation.
Another example copies files into a backup directory:
printf '%s\n' config.ini app.conf | xargs -I {} cp -- {} backup/
The generated commands are conceptually:
cp -- config.ini backup/
cp -- app.conf backup/
For simple commands where arguments can appear at the end, -I is often unnecessary. For example:
printf '%s\n' file1 file2 | xargs rm --
is simpler than:
printf '%s\n' file1 file2 | xargs -I {} rm -- {}
Parallel Execution with -P
The -P option allows multiple command invocations to run concurrently:
printf '%s\n' archive1 archive2 archive3 archive4 | xargs -n 1 -P 4 gzip
In this example:
-n 1gives one archive name to eachgzipinvocation.-P 4permits up to fourgzipprocesses to run at the same time.
Parallel execution can reduce processing time for independent CPU-bound or I/O-bound tasks, but it should not be used when commands depend on running in a specific order or modify the same resource.
Filenames, Whitespace, and the -0 Option
The most important safety issue with xargs is filename parsing.
By default, xargs treats blanks, tabs, newlines, quotes, and backslashes as input syntax. Therefore, this pattern is unsafe for arbitrary filenames:
find . -type f | xargs rm
A pathname such as:
./old reports/report 1.txt
contains spaces and can be interpreted incorrectly.
A robust pattern is to use NUL-delimited records:
find . -type f -print0 | xargs -0 rm --
Here:
find ... -print0terminates each pathname with a NUL byte.xargs -0reads NUL-separated items.- Spaces, tabs, quotes, and newlines inside filenames remain part of the filename.
This is the preferred find + xargs pattern when arbitrary pathnames are possible.
The Meaning of --
Many Unix commands use -- to mark the end of command options. Arguments appearing after it are treated as operands even if they begin with -.
Consider a file literally named:
-rf
This is dangerous:
rm -rf
because rm interprets -rf as options.
To explicitly treat it as a filename:
rm -- -rf
The same rule applies when xargs invokes rm:
printf '%s\n' '-rf' | xargs rm --
In this command, the -- belongs to rm, not to xargs. xargs constructs a command similar to:
rm -- -rf
Many tools support this convention, including rm, cp, mv, grep, and others.
Using find with xargs
A common pattern is to let find select files and xargs invoke another command.
Compress all .log files:
find logs -type f -name '*.log' -print0 | xargs -0 -n 1 gzip
Remove matching temporary files:
find . -type f -name '*.tmp' -print0 | xargs -0 rm --
Calculate checksums in parallel:
find images -type f -print0 | xargs -0 -n 1 -P 4 sha256sum
find -exec Versus xargs
The find command can execute commands directly, so xargs is not always necessary.
Run once per file:
find . -type f -name '*.tmp' -exec rm -- {} \;
Batch many files into each invocation:
find . -type f -name '*.tmp' -exec rm -- {} +
The {} + form is often a clean alternative to:
find . -type f -name '*.tmp' -print0 | xargs -0 rm --
Both approaches safely handle unusual filenames.
A practical comparison is:
| Approach | Characteristics |
|---|---|
find ... -exec command {} \; |
Executes once per pathname; simple but may create many processes. |
find ... -exec command {} + |
Batches pathnames safely without requiring xargs. |
find ... -print0 | xargs -0 command |
Batches safely and provides features such as -P parallelism. |
Shell Globbing Versus xargs
Sometimes xargs is unnecessary because the shell can already generate the argument list.
To remove entries beginning with soi in the current directory:
rm -rf -- soi*
The shell expands soi* before rm runs, producing a command conceptually similar to:
rm -rf -- soi-audio soi-beards soi-maps soi-minimap
A pipeline such as:
ls | grep soi | xargs rm -rf --
is less robust. Parsing ls output is generally unnecessary, and unusual filenames can make text-based pipelines ambiguous. When a shell glob expresses the selection directly, prefer the glob.
Previewing Commands Before Running Them
Before using xargs with destructive commands, replace the destructive command with printf or echo, or use xargs -t.
For example:
find . -type f -name '*.tmp' -print0 | xargs -0 -t rm --
The -t option prints commands as they are executed.
For an entirely non-destructive preview:
find . -type f -name '*.tmp' -print0 | xargs -0 -n 1 printf 'would remove: %s\n'
This makes it easier to confirm the selected pathnames before deleting anything.
Empty Input
An important detail is what happens when the producer returns no items.
On GNU systems, xargs normally runs the command once even when there is no input. GNU xargs -r prevents this:
find . -type f -name '*.tmp' -print0 | xargs -0 -r rm --
For scripts that need portability across systems where -r may not exist, find -exec ... {} + is often a convenient alternative.
Practical Patterns
Change Permissions on Matching Files
find public -type f -name '*.html' -print0 | xargs -0 chmod 644
Search a Generated File List
find logs -type f -name '*.log' -print0 | xargs -0 grep -n -- 'ERROR'
Run Four Jobs in Parallel
printf '%s\n' host1 host2 host3 host4 | xargs -n 1 -P 4 ping -c 1
Process One Item at a Time with a Placeholder
printf '%s\n' alpha beta gamma | xargs -I {} sh -c 'printf "item: %s\n" "$1"' _ {}
When a shell command is needed inside xargs, pass the item as a positional parameter rather than interpolating untrusted text directly into shell code.
Common Mistakes
I. Parsing ls output
Avoid:
ls | grep '.log' | xargs rm
Prefer a shell glob when possible:
rm -- *.log
or use find with NUL delimiters:
find . -maxdepth 1 -type f -name '*.log' -print0 | xargs -0 rm --
II. Forgetting unusual filenames
Avoid newline-delimited filename processing when filenames may contain spaces or newlines. Use -print0 together with -0.
III. Confusing pipe input with arguments
This:
printf '%s\n' file1 file2 | rm
does not make file1 and file2 arguments to rm.
This does:
printf '%s\n' file1 file2 | xargs rm --
IV. Assuming -- belongs to xargs
In:
xargs rm -rf --
xargs is being asked to run the command rm -rf -- .... The -- terminates option processing for rm.
V. Using parallel execution for dependent operations
Do not use -P when one invocation must finish before the next one starts.
Challenges
- Use
printfandxargsto create three directories namedalpha,beta, andgamma. - Use
xargs -n 2to print six words two at a time. Observe how many times the command runs. - Use
xargs -I {}to transform a list of usernames into messages such aschecking alice. - Create files whose names contain spaces, then compare plain
find | xargswith the safefind -print0 | xargs -0form. - Find all
.tmpfiles below a test directory and preview their deletion without deleting them. - Repeat the previous task using
find -exec ... {} +instead ofxargs. - Use
xargs -P 4to calculate checksums for several files concurrently. - Create a file whose name begins with
-and remove it safely usingrm -- filename. - Explain the difference between
command1 | command2andcommand1 | xargs command2in terms of standard input and command arguments. - For a set of files sharing a simple prefix, compare a shell glob with an
ls | grep | xargspipeline and identify which approach is simpler and safer.