Linux xargs Command
last modified October 23, 2026
The xargs command in Linux reads items from standard input and
runs a command with those items as arguments. It bridges commands that
produce lists, such as find, grep -l, or
ls, with commands that expect arguments, such as
rm, wc, or gzip. Many commands do not
read file names from standard input, so a plain pipe is not enough; this is
where xargs comes in.
The syntax is xargs [options] [command [initial-arguments]].
xargs collects as many input items as fit on a command line,
runs the command, and repeats until the input is exhausted. It can also run
commands in parallel. The examples use GNU xargs from findutils
4.9.
Table of contents
- Basics: basic usage, file lists, arguments per command, tracing
- Input parsing: spaces in names, quotes and backslashes, custom delimiters
- Building commands: replace strings, shell commands safely, empty input, confirmation
- Execution: parallel runs, process slots, command line limits, argument list too long, exit status
- Usage: reading from a file, recipes
Basic usage
xargs splits its input into items at blanks and newlines and
appends them to the command. Without a command, it uses
echo.
echo one two three | xargs seq 5 | xargs seq 5 | xargs echo numbers:
The five lines produced by seq become five arguments of a single
echo, so they are printed on one line. Arguments given after the
command, such as numbers:, come before the input items.
$ echo one two three | xargs one two three $ seq 5 | xargs 1 2 3 4 5 $ seq 5 | xargs echo numbers: numbers: 1 2 3 4 5
Passing file lists
The typical use is turning a list of file names into arguments.
ls logs/*.log | xargs wc -l
wc receives the three file names as arguments, exactly as if we
had typed them. Since wc is run only once, it also prints a
total.
$ ls logs/*.log | xargs wc -l 100 logs/app1.log 200 logs/app2.log 300 logs/app3.log 600 total
Arguments per command
The -n option sets the maximum number of items per command, and
-L the number of input lines per command.
seq 6 | xargs -n 2 seq 6 | xargs -n 4 echo printf "a b\nc\nd e f\n" | xargs -L 1 echo
With -n 2, echo runs three times. The last group may
be smaller. -L 1 keeps the items of each line together, which is
useful for input where a line represents one record.
$ seq 6 | xargs -n 2 1 2 3 4 5 6 $ seq 6 | xargs -n 4 echo 1 2 3 4 5 6 $ printf "a b\nc\nd e f\n" | xargs -L 1 echo a b c d e f
Tracing commands
The -t option prints each command to standard error before
running it.
seq 4 | xargs -t -n 2 echo
Tracing shows how xargs groups the input, which helps when
building more complex commands. A safe way to test a destructive command is
to put echo in front of it, as in xargs echo rm.
$ seq 4 | xargs -t -n 2 echo echo 1 2 1 2 echo 3 4 3 4
File names with spaces
Because xargs splits at blanks, file names containing spaces are
broken into pieces. The -0 option uses NUL characters as
separators instead.
find docs -type f | xargs wc -l find docs -type f -print0 | xargs -0 wc -l find docs -type f | xargs -d "\n" wc -l
In the first command, meeting notes.txt becomes two arguments
and wc fails. find -print0 together with
xargs -0 is the robust solution, since file names cannot contain
NUL. Other commands produce NUL-separated output as well, such as
grep -lZ and sort -z. Splitting only at newlines
with -d "\n" handles spaces too, but not the rare names that
contain newlines.
$ find docs -type f | xargs wc -l wc: docs/meeting: No such file or directory wc: notes.txt: No such file or directory 1 docs/plan.txt 1 total $ find docs -type f -print0 | xargs -0 wc -l 1 docs/meeting notes.txt 1 docs/plan.txt 2 total $ find docs -type f | xargs -d "\n" wc -l 1 docs/meeting notes.txt 1 docs/plan.txt 2 total
Quotes and backslashes
In its default mode, xargs interprets single quotes, double
quotes, and backslashes in the input, similar to a shell.
echo "it's here" | xargs echo echo "\"quoted words\" plain" | xargs -n 1 echo echo "a\\b" | xargs echo
An apostrophe in a file name or text starts a quoted string, and
xargs stops with an error when the quote is not closed. Quoted
words form one item, and backslashes are removed. This behaviour is another
reason to use -0 or -d, which turn off all quote
processing.
$ echo "it's here" | xargs echo xargs: unmatched single quote; by default quotes are special to xargs unless you use the -0 option $ echo "\"quoted words\" plain" | xargs -n 1 echo quoted words plain $ echo "a\\b" | xargs echo ab
Custom delimiters
The -d option sets a custom item delimiter.
echo "red,green,blue" | xargs -d , -n 1 echo printf "red,green,blue" | xargs -d , -n 1 echo
With a custom delimiter, the newline is an ordinary character. The output of
echo ends with a newline, so the last item is
blue followed by a newline, which shows up as an empty line.
printf without \n avoids the problem.
$ echo "red,green,blue" | xargs -d , -n 1 echo red green blue $ printf "red,green,blue" | xargs -d , -n 1 echo red green blue
Replace strings
The -I option defines a placeholder that is replaced with the
input item. It allows placing the item anywhere in the command, even several
times.
ls logs/*.log | xargs -I {} cp {} backup/
cat names.txt | xargs -I NAME echo "Hello, NAME!"
-I runs the command once per input line, and the whole line is
the item, so names with spaces are preserved. The placeholder can be any
string; {} is a convention. For copying into a directory,
xargs cp -t backup/ is more efficient, since it copies all files
in one call.
$ mkdir backup
$ ls logs/*.log | xargs -I {} cp {} backup/
$ ls backup
app1.log
app2.log
app3.log
$ cat names.txt | xargs -I NAME echo "Hello, NAME!"
Hello, alpha!
Hello, beta!
Hello, gamma!
Hello, delta!
Hello, epsilon!
Running shell commands safely
To run several commands or use shell features for each item,
xargs starts a shell with sh -c. The item must be
passed as an argument of the shell, not inserted into the script text.
ls x* | xargs -I {} sh -c 'echo processing {}'
ls x* | xargs -I {} sh -c 'echo processing "$1"' sh {}
seq 3 | xargs sh -c 'for f; do echo "item $f"; done' sh
We create a file named x; echo INJECTED. In the first command,
the name becomes part of the shell script, so the shell executes the
injected command. This is a security hole when file names come from
untrusted sources. In the second command, the name is passed as the
positional parameter $1 and is treated as data. The word
sh after the script fills $0. The last form
processes many items in one shell by looping over the positional
parameters.
$ touch "x; echo INJECTED"
$ ls x* | xargs -I {} sh -c 'echo processing {}'
processing x
INJECTED
$ ls x* | xargs -I {} sh -c 'echo processing "$1"' sh {}
processing x; echo INJECTED
$ seq 3 | xargs sh -c 'for f; do echo "item $f"; done' sh
item 1
item 2
item 3
Empty input
By default, GNU xargs runs the command once even if the input is
empty. The -r option prevents this.
find . -name "*.tmp" | xargs ls find . -name "*.tmp" | xargs -r ls
No .tmp file exists, so ls runs without arguments
and lists the current directory. With a command like rm or
chmod, running without arguments can do something unexpected,
so -r (--no-run-if-empty) belongs in most
scripts.
$ find . -name "*.tmp" | xargs ls backup docs logs names.txt urls.txt $ find . -name "*.tmp" | xargs -r ls
Asking for confirmation
The -p option prints each command and asks for confirmation
before running it.
echo backup/app1.log | xargs -p rm
We answered y, and the file was removed. Any answer not starting
with y or Y skips the command. The prompt reads from
the terminal, so it works even though standard input is the pipe.
$ echo backup/app1.log | xargs -p rm rm backup/app1.log?...y $ ls backup app2.log app3.log
Parallel execution
The -P option runs up to the given number of commands at the
same time.
start=$SECONDS; seq 4 | xargs -n 1 sleep; echo "sequential: $((SECONDS - start)) s" start=$SECONDS; seq 4 | xargs -n 1 -P 4 sleep; echo "parallel: $((SECONDS - start)) s" printf "%s\n" 3 1 2 | xargs -n 1 -P 3 sh -c 'sleep "$1"; echo "finished $1"' sh
Sequentially, the four sleeps take 1 + 2 + 3 + 4 = 10 seconds; in parallel,
they take as long as the longest one. We measure the time with the Bash
SECONDS variable. Each command needs its own items, so
-P is combined with -n or -I. The
output of parallel commands appears in the order they finish, not in the
input order, and lines from different commands can interleave.
-P 0 runs as many processes as possible; the
nproc command prints the number of CPUs as a sensible value.
$ start=$SECONDS; seq 4 | xargs -n 1 sleep; echo "sequential: $((SECONDS - start)) s" sequential: 10 s $ start=$SECONDS; seq 4 | xargs -n 1 -P 4 sleep; echo "parallel: $((SECONDS - start)) s" parallel: 4 s $ printf "%s\n" 3 1 2 | xargs -n 1 -P 3 sh -c 'sleep "$1"; echo "finished $1"' sh finished 1 finished 2 finished 3
Process slots
The --process-slot-var option sets an environment variable to
the number of the slot each parallel command runs in.
seq 1 6 | xargs -n 1 -P 2 --process-slot-var=SLOT sh -c 'echo "slot $SLOT got $1"; sleep 0.2' sh | sort
With two parallel processes, the slots are 0 and 1. A slot number can be used to give each worker its own resource, such as an output file or a database connection.
$ seq 1 6 | xargs -n 1 -P 2 --process-slot-var=SLOT sh -c 'echo "slot $SLOT got $1"; sleep 0.2' sh | sort slot 0 got 1 slot 0 got 3 slot 0 got 5 slot 1 got 2 slot 1 got 4 slot 1 got 6
Command line limits
The kernel limits the total size of arguments and environment of a new
process. xargs respects this limit by splitting the input into
several invocations.
xargs --show-limits < /dev/null 2>&1 | head -n 4 getconf ARG_MAX seq 1 200000 | xargs echo | wc -l seq 1 20 | xargs -s 30 echo
The limits are printed to standard error, so we redirect it to see the first
lines. On this system, a command line can be about 2 MB long. Two hundred thousand
numbers do not fit in one command, so echo runs ten times. The
-s option sets a smaller maximum command line size in bytes,
which we use to make the splitting visible.
$ xargs --show-limits < /dev/null 2>&1 | head -n 4 Your environment variables take up 353 bytes POSIX upper limit on argument length (this system): 2094751 POSIX smallest allowable upper limit on argument length (all systems): 4096 Maximum length of command we could actually use: 2094398 $ getconf ARG_MAX 2097152 $ seq 1 200000 | xargs echo | wc -l 10 $ seq 1 20 | xargs -s 30 echo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Argument list too long
When a shell expansion produces more arguments than the limit allows, the
command cannot start. xargs avoids the error.
/bin/echo $(seq 1 1000000) | wc -c seq 1 1000000 | xargs /bin/echo | wc -c
The same happens with wildcards in directories with very many files, as in
rm *.log. Piping the names through xargs, or using
find -exec ... +, splits them into acceptable batches.
$ /bin/echo $(seq 1 1000000) | wc -c bash: /bin/echo: Argument list too long 0 $ seq 1 1000000 | xargs /bin/echo | wc -c 6888896
Exit status
The exit status of xargs summarizes the commands it ran.
printf "%s\n" logs/app1.log missing.log | xargs ls > /dev/null; echo "exit: $?" echo x | xargs nosuchcommand; echo "exit: $?" seq 3 | xargs -n 1 sh -c 'echo "run $1"; [ "$1" -ne 2 ] || exit 255' sh; echo "exit: $?"
Status 123 means that at least one invocation failed with a status from 1 to
125. Status 127 means the command was not found. When a command exits with
255, xargs stops immediately and returns 124; the third item is
never processed.
$ printf "%s\n" logs/app1.log missing.log | xargs ls > /dev/null; echo "exit: $?" ls: cannot access 'missing.log': No such file or directory exit: 123 $ echo x | xargs nosuchcommand; echo "exit: $?" xargs: nosuchcommand: No such file or directory exit: 127 $ seq 3 | xargs -n 1 sh -c 'echo "run $1"; [ "$1" -ne 2 ] || exit 255' sh; echo "exit: $?" run 1 run 2 xargs: sh: exited with status 255; aborting exit: 124
Reading items from a file
The -a option reads the items from a file instead of standard
input.
xargs -a names.txt -n 2 echo
xargs -a urls.txt -n 1 -P 3 curl -s -o /dev/null -w "%{http_code} %{url_effective}\n" | sort
Keeping standard input free matters for commands that read from it. The
second command checks three URLs in parallel with curl and
prints their status codes. For interactive programs such as editors, the
-o option reopens standard input as the terminal.
$ xargs -a names.txt -n 2 echo
alpha beta
gamma delta
epsilon
$ xargs -a urls.txt -n 1 -P 3 curl -s -o /dev/null -w "%{http_code} %{url_effective}\n" | sort
200 https://httpbin.org/robots.txt
200 https://httpbin.org/user-agent
200 https://httpbin.org/uuid
Practical recipes
Some common combinations.
grep -l "^1$" logs/*.log | xargs -r sed -i 's/^1$/one/' find logs -name "*.log" -print0 | xargs -0 -P 2 -n 1 gzip pgrep -f "sleep 30[01]" | xargs -r kill
The first command edits only the files that contain a match. The second
compresses log files in parallel. The third terminates processes found by
pgrep; -r prevents running kill without
arguments when nothing matches.
$ grep -l "^1$" logs/*.log | xargs -r sed -i 's/^1$/one/' $ head -n 1 logs/*.log ==> logs/app1.log <== one ==> logs/app2.log <== one ==> logs/app3.log <== one $ find logs -name "*.log" -print0 | xargs -0 -P 2 -n 1 gzip $ ls logs app1.log.gz app2.log.gz app3.log.gz $ sleep 300 & sleep 301 & $ pgrep -f "sleep 30[01]" | xargs -r kill $ pgrep -fc "sleep 30[01]" 0
xargs versus find -exec
For files found by find, -exec command {} + batches
the arguments just like xargs and handles any file name safely,
without the need for -print0. xargs is the better
choice when the list comes from another command, when commands should run in
parallel with -P, or when the number of items per command must
be controlled. See the find command
tutorial for -exec.
Best practices for xargs
- Use NUL separators: Combine
find -print0orgrep -lZwithxargs -0. - Add -r: Avoid running commands with no arguments.
- Never embed {} in sh -c: Pass items as positional parameters.
- Preview first: Use
-t,-p, orechobefore destructive commands. - Parallelize carefully: Use
-Pwith-nand expect unordered output. - Check the exit status: 123, 124, 125, 126, and 127 indicate problems.
Source
In this article, we have used the xargs command to build
command lines from input, handle spaces, quotes, and delimiters, place
arguments with replace strings, run shell commands safely, run commands in
parallel, deal with command line limits, and interpret its exit status.
Author
List all Linux tutorials.