macOS `xargs: invalid option -- d` / `-- i`: one item per line with `tr '\n' '\0' | xargs -0`, and `-I {}` failing with "command line cannot be assembled, too long"
A script that hands a list of file names to xargs, one per line, stops on
a Mac:
$ mkdir demo && cd demo
$ printf 'my file.txt\nother.txt\n' > list.txt
$ xargs -d '\n' -n1 echo < list.txt; echo "exit $?"
xargs: invalid option -- d
usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements] [-S replsize]]
[-J replstr] [-L number] [-n number [-x]] [-P maxprocs]
[-s size] [utility [argument ...]]
exit 1
$ xargs -i echo "[{}]" < list.txt; echo "exit $?"
xargs: invalid option -- i
usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements] [-S replsize]]
[-J replstr] [-L number] [-n number [-x]] [-P maxprocs]
[-s size] [utility [argument ...]]
exit 1
-d (delimiter) and -i (lowercase, “replace {}”) are GNU xargs options
(from GNU’s documentation; I didn’t run GNU xargs here). macOS ships BSD
xargs, which has neither. The usage line above lists what it
does take. There’s no --version; what shows the build:
$ xargs --version; echo "exit $?"
xargs: unrecognized option `--version'
usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements] [-S replsize]]
[-J replstr] [-L number] [-n number [-x]] [-P maxprocs]
[-s size] [utility [argument ...]]
exit 1
$ what /usr/bin/xargs
/usr/bin/xargs:
PROGRAM:xargs PROJECT:shell_cmds-329
PROGRAM:xargs PROJECT:shell_cmds-329
Why not just drop -d
Without -d, xargs splits on blanks and reads quotes:
$ xargs -n1 echo < list.txt
my
file.txt
other.txt
$ printf "it's.txt\n" | xargs echo; echo "exit $?"
xargs: unterminated quote
exit 1
my file.txt became two arguments, and an apostrophe in a name stopped
xargs before it ran anything.
One item per line: tr '\n' '\0' | xargs -0
With -0, xargs splits only on NUL; in the tests below it also left quotes
and backslashes alone. Turning newlines into NULs first gives one argument
per line:
$ tr '\n' '\0' < list.txt | xargs -0 -n1 echo
my file.txt
other.txt
$ printf "it's.txt\n" | tr '\n' '\0' | xargs -0 echo
it's.txt
A last line without a final newline still came through:
$ printf 'a\nb' | tr '\n' '\0' | xargs -0 -n1 echo
a
b
If the list comes from find, -print0 writes the NULs itself:
$ mkdir names && touch "names/my file.txt" "names/it's.txt"
$ find names -type f -print0 | xargs -0 -n1 echo
names/it's.txt
names/my file.txt
Instead of -i: -I {}
-I takes the placeholder as an argument and runs the command once per
line:
$ xargs -I {} echo "[{}]" < list.txt
[my file.txt]
[other.txt]
On its own, -I still treated the input differently from the raw line in
these cases: it dropped leading blanks, read quotes, and ate a backslash.
With tr and -0 in front, the line came through as it was:
$ printf ' lead.txt\n' | xargs -I {} echo "[{}]"
[lead.txt]
$ printf ' lead.txt\n' | tr '\n' '\0' | xargs -0 -I {} echo "[{}]"
[ lead.txt]
$ printf "it's.txt\n" | xargs -I {} echo "[{}]"; echo "exit $?"
xargs: unterminated quote
exit 1
$ printf 'a\\b\n' | xargs -I {} echo "[{}]"
[ab]
$ printf 'a\\b\n' | tr '\n' '\0' | xargs -0 -I {} echo "[{}]"
[a\b]
-I and “command line cannot be assembled, too long”
BSD xargs -I has a separate size limit for the replaced argument. The man
page:
$ man xargs | col -b | grep -A2 '^ *-S replsize'
-S replsize
Specify the amount of space (in bytes) that -I can use for
replacements. The default for replsize is 255.
In my tests, an argument of 254 bytes after replacement worked and 255 did
not. Lines of 254 and 255 x characters (wc -c counts echo’s newline
too):
$ printf '%254s\n' | tr ' ' x | xargs -I {} echo {} | wc -c
255
$ printf '%255s\n' | tr ' ' x | xargs -I {} echo {} | wc -c
xargs: command line cannot be assembled, too long
0
The text around {} counts. With pre- in front, 250 characters of input
worked and 251 failed:
$ printf '%250s\n' | tr ' ' x | xargs -I {} echo "pre-{}" | wc -c
255
$ printf '%251s\n' | tr ' ' x | xargs -I {} echo "pre-{}" | wc -c
xargs: command line cannot be assembled, too long
0
When one long line sits in the middle of the input, xargs stopped there. The lines after it didn’t run, and the exit status was 1:
$ { echo a; printf '%300s\n' | tr ' ' x; echo b; } > mixed.txt
$ xargs -I {} echo "[{}]" < mixed.txt; echo "exit $?"
[a]
xargs: command line cannot be assembled, too long
exit 1
-0 doesn’t change this limit. -S raises it:
$ tr '\n' '\0' < mixed.txt | xargs -0 -I {} echo "[{}]" > out0.txt; echo "exit $?"
xargs: command line cannot be assembled, too long
exit 1
$ cat out0.txt
[a]
$ tr '\n' '\0' < mixed.txt | xargs -0 -S 4096 -I {} echo "[{}]" | cut -c1-10
[a]
[xxxxxxxxx
[b]
A large value is accepted with a short line:
$ printf 'a\n' | xargs -S 1000000 -I {} echo got {}; echo "exit $?"
got a
exit 0
I didn’t look for an upper limit beyond that.
Checklist
xargs: invalid option -- d/-- ion macOS:/usr/bin/xargsis BSD xargs.- Without
-d, a space in a name splits it, and an apostrophe stops xargs with “unterminated quote”. - One item per line:
tr '\n' '\0' < list | xargs -0 ...(orfind ... -print0 | xargs -0). -ibecomes-I {}. Puttr '\n' '\0' | xargs -0in front if leading blanks, quotes, or backslashes must survive.- With
-I, a replaced argument of 255 bytes or more failed here with “command line cannot be assembled, too long”. xargs stopped at that line and exited 1. Add-Swith room for your longest line (-S 4096here).
Tested on macOS 26.5.2 (Apple silicon) with /usr/bin/xargs
(PROGRAM:xargs PROJECT:shell_cmds-329) in zsh 5.9 (arm64-apple-darwin25.0).
GNU findutils xargs and Linux were not tested.