OFFSET偏移
← All articlesOFFSET / FIELD NOTESNO. 044

Passing arguments with xargs

Convert standard input into command arguments while handling delimiters, placeholders, parallelism, and empty input.

English translation · Code and commands follow the Chinese original.

IN THESE READING PATHSLinux command-line fundamentals and composition ↗
About 4 min readStart reading

In the vast Unix/Linux ecosystem, combining tools is an art. We know the power of the pipe (|): like a quiet river, it carries an upstream program's standard output (stdout) smoothly into a downstream program's standard input (stdin). Real-world system interactions are often more complicated, though. Many classic commands, such as rm, cp, and mkdir, do not listen to this "river". They turn their backs on the pipe and wait for explicit command-line arguments.

This raises a classic design problem: how do you turn a flowing data stream into discrete command-line arguments?

That is precisely why xargs exists. It is more than a command runner: it is a diligent "translator" and "scheduler".

1. The core idea: stdin → command-line arguments#

The core job of xargs is to read data from standard input, parse it into separate arguments, and append those arguments to the target command before running it.

Imagine that you want to use find to locate every .log file and delete them.

Your first instinct might be to write:

find . -name "*.log" | rm          # 失败!rm 不管 stdin

Because rm ignores standard input, it simply waits for command-line arguments.

But when you introduce xargs:

find . -type f -name "*.log" -print0 | xargs -0 -r -p rm --  # 核对每批待删除参数后确认

It turns input items into an argument list such as rm a.log b.log c.log. By default, parsing follows whitespace, quote, and backslash rules rather than simply processing one line at a time; NUL delimiters are used here to avoid ambiguous filenames. Compared with the find -exec you have already learned, xargs is more general: it is not limited to find and can work with any command that produces a list.

2. Handle spaces and special characters: -print0 and -0#

Filenames can contain spaces or newlines. Splitting on whitespace, as xargs does by default, can be disastrous. Combine find's -print0 with xargs' -0:

find . -type f -name "*.bak" -print0 | xargs -0 -r -p rm --

The null character (\0) becomes the sole, unambiguous delimiter, avoiding all the special-character pitfalls.

3. Placeholders: precise argument placement (-I)#

Not every command accepts arguments at the end. -I {} lets you insert an argument wherever you need it:

# 将找到的文件移动到归档目录
find . -type f -name "*.mp4" -print0 | xargs -0 -r -p -I {} mv -- {} /archive/videos/

# 批量创建目录
printf '%s\n' a b c | xargs -r -I {} mkdir -p -- "$HOME/projects/{}"

-I {} normally runs once per input line; it will not split echo "a b c" into three directories. The original form would create a single directory named a b c. When combined with -0, substitution is performed for each NUL-delimited item; the destination archive directory must already exist. -I is generally mutually exclusive with -n and -L, so do not combine them blindly.

4. Parallel execution: -P and -n#

xargs can act as a lightweight task scheduler, running tasks in parallel:

cat urls.txt | xargs -r -n 1 -P 4 wget --
  • -n 1: Pass only 1 URL to wget per invocation
  • -P 4: Run 4 wget processes at a time for parallel downloads

A few arguments provide concurrency without having to write a multithreaded shell script.

5. Guard against empty input: -r#

If the upstream command provides no data, xargs still runs the target command once without arguments by default, which may cause an error. -r (--no-run-if-empty) prevents this:

grep -lZ "TODO" * | xargs -0 -r -o nvim --    # 没找到就不开 nvim

Everyday use: quick reference#

I want to... Command
Delete files in bulk find . -type f -name "*.log" -print0 | xargs -0 -r -p rm --
Delete safely (filenames containing spaces) find . -type f -name "*.tmp" -print0 | xargs -0 -r -p rm --
Move files in bulk find . -type f -name "*.mp4" -print0 | xargs -0 -r -p -I {} mv -- {} /target/
Download in parallel cat urls.txt | xargs -r -n 1 -P 4 wget --
Do nothing when the search returns no results grep -lZ "TODO" * | xargs -0 -r -o nvim --


The empty-input behavior described here applies to GNU xargs. -r is a GNU extension, and other implementations may behave differently by default. -o lets an interactive program read input from the terminal; -p requests confirmation before execution. NUL delimiters preserve filename boundaries but do not eliminate races between finding a file and acting on it. Permissions and race conditions need separate consideration in untrusted directories that can be modified concurrently. The URL list assumes that no item contains unencoded whitespace.

References & further reading

  1. GNU xargs options ↗

Adapted from technical notes. Examples use generic names.

Continue along a reading path

Linux command-line fundamentals and composition

  1. 01File and directory operations
  2. 02Process and job management
  3. 03Text processing and system queries
  4. 04Shell fundamentals
  5. 05Fuzzy finding with fzf
  6. 06Passing arguments with xargs
CONTINUE READING / NEXT ARTICLESSH authentication: directories, keys, and TOFU

Distinguish user identity, server identity, and the limits of trust on first use.