xa
A modern, safe replacement for xargs.
xa reads items from stdin and runs a command for each one. The key difference from xargs: it uses null-delimited input by default, which means filenames with spaces, newlines, or special characters work correctly without extra flags.
Installation
The binary installs as xa. Or download a pre-built binary from the releases page.
Shell completions (bash, zsh, fish) and a man page are included in each release.
Quick start
# Find files and process them (pairs naturally with fd's -0 flag)
|
# Parallel processing with 4 workers
|
# Preview what would run without executing
|
Why xa instead of xargs?
xargs |
xa |
|
|---|---|---|
| Default delimiter | whitespace | null (\0) |
| Safe with spaces in filenames | only with -0 |
always |
| Placeholders | {} only |
{} {/} {.} {/.} {ext} {#} {slot} |
| Parallel workers | -P n |
-j n |
| Ordered output in parallel | no | -k |
| Retry on failure | no | --retry n |
| Rate limiting | no | --rate n |
| JSON structured logging | no | --json-log |
| Dry run | no | --dry-run |
| Progress bar | no | -p |
The null-delimiter default is the most important difference. With xargs, a file named my photo.jpg silently becomes two arguments — my and photo.jpg. With xa, it works correctly out of the box as long as your input source also uses null delimiters (which find -print0, fd -0, and git ls-files -z all support).
Placeholders
When a placeholder appears in the command, xa substitutes it for each input item. If no placeholder is present, the item is appended as a trailing argument.
| Placeholder | Expands to | Example input | Result |
|---|---|---|---|
{} |
the item as-is | /path/to/photo.png |
/path/to/photo.png |
{/} |
basename | /path/to/photo.png |
photo.png |
{.} |
path without extension | /path/to/photo.png |
/path/to/photo |
{/.} |
basename without extension | /path/to/photo.png |
photo |
{ext} |
extension only | /path/to/photo.png |
png |
{#} |
1-based item index | — | 1, 2, 3, … |
{slot} |
0-based worker slot | — | 0, 1, … (with -j) |
# Convert images, preserving directory structure
|
# Show only filenames
|
# Rename by extension
|
Parallelism
# Run 8 workers
|
# Keep output in input order even when running in parallel
|
# Stop immediately if any command fails
|
Batching
-n <N> passes N items per command invocation instead of one at a time.
# Pass 10 files to each invocation of a command
|
# All items in a single invocation (-n0)
|
With a placeholder, each item's args are repeated within the single invocation:
# xa -n2 -- echo {} expands to: echo item1 item2, then echo item3 item4, ...
|
# a b
# c d
Input modes
# Default: null-delimited (safest)
|
# Newline-delimited
|
# Custom delimiter
|
# Whitespace-split with quoting (legacy xargs behaviour)
|
# hello world
# bye
Resilience
# Retry failed commands up to 3 times with exponential backoff
# Stop after the first failure
# Limit to 10 commands per second
# Confirm before each command
Output options
# Print each command before running it
# Prefix each output line with the source item
# Emit structured JSON to stderr for each completed command
# Show a progress bar
Shell mode
--shell (-S) wraps the command in sh -c, enabling pipes and other shell features:
|
Comparison with similar tools
- xargs — the classic. Whitespace-splitting default makes it unsafe with most filenames. xa is a drop-in improvement for the common
find | xargspattern. - GNU parallel — extremely feature-rich but a large Perl dependency. xa covers the most common 90% of use cases in a single ~2 MB static binary.
- fd's built-in exec (
fd --exec) — convenient for single commands but no parallelism control, no retry, no rate limiting, no JSON logging.
Benchmarks
Benchmarks run on a Linux x86-64 machine with cargo bench (criterion). These measure wall-clock time for process spawning + execution, so they reflect real-world overhead.
| Workload | Throughput |
|---|---|
Sequential echo — 100 items, 1 worker |
~3,350 items/s |
Parallel echo — 100 items, 4 workers |
~12,380 items/s |
Batch echo — 100 items, -n10 (10 invocations) |
~28,800 items/s |
| Dry-run — 1,000 items, placeholder expansion only | ~550,000 items/s |
Parallelism (-j4) gives roughly a 3.7× throughput improvement for CPU-bound or I/O-bound workloads. Batch mode (-n10) reduces process-spawn overhead by ~8.6× for commands that accept multiple arguments. The dry-run numbers show that placeholder expansion itself adds negligible overhead (~1.8 µs per 1,000 items).
Run benchmarks yourself:
# HTML report: target/criterion/report/index.html
Exit codes
| Code | Meaning |
|---|---|
0 |
all commands succeeded |
1 |
one or more commands failed |
2 |
xa itself failed (bad arguments, I/O error) |
130 |
interrupted by Ctrl-C |