-r
is not to ignore empty elements, it is to avoid running the command once if the input has no element (it's not needed with -I
(nor BSD's -J
) though).
You generally want -r
for that reason.
find . -criteria -print0 | xargs -0 ls -ld
If find
didn't find anything would still run ls -d
without argument, and would list .
.
So you do want:
find . -criteria -print0 | xargs -r0 ls -ld
For ls
not to be run at all if find
finds nothing.
Note that both -r
and -0
are GNU extensions (same for -d
which is even less portable). -r
is the default behaviour in the xargs
of some BSDs including FreeBSD's and NetBSD's (though that makes them non-POSIX compliant).
(in any case, no need for xargs
in that particular example as you can do find . -criteria -exec ls -ld {} +
).
As far as I know, GNU xargs
is the only implementation that supports a -d
option, and it has no support for filtering out specific argument values, be that empty ones or otherwise, so using grep
(grep -v '^$'
or LC_ALL=C grep .
) is the way to go.
If you still wanted to use -a
(another GNU extension), for instance so as to leave stdin untouched for the command, and with a shell with support for ksh-style process substitution (ksh, zsh, bash), you could do:
xargs -rd '\n' -a <(grep -v '^$' test.txt) cmd --
xargs -d '\n' -I{} -a <(grep -v '^$' test.txt) cmd -- {}
(the rc, akanga, es, fish and yash shells have equivalent features with different syntax).
Note that -0
is like -d '\0'
, so conflicts with -d '\n'
. You have to choose which d
elimiter you want. The latter will take precedence in -0 -d '\n'
as it comes last.
-0
is the preferred delimiter as that's the one byte value that cannot occur in a command's argument or a file's path. -0
, contrary to -d
is now found in many other xargs
implementations (including those of most BSDs, Solaris, busybox, toybox, ast-open). With xargs
implementations that don't support -d
(that is, all but GNU xargs
), you can use:
tr '\n' '\0' | xargs -0 ...
In place of GNU's:
xargs -d '\n' ...
If using zsh
, instead of using xargs
, you could do:
for arg ( ${(f)"$(<test.txt)"} ) cmd -- $arg
The f
parameter expansion flag splits on newlines, and as ${...}
is left unquoted, empty elements are removed.
With bash
:
readarray -t args < test.txt &&
for arg in "${args[@]}"; do
[ -z "$arg" ] || cmd -- "$arg"
done
You could also do empty removal there by leaving the array expansion unquoted, but you'd also need to disable globbing and splitting which are also done upon unquoted parameter expansions:
IFS=; set -o noglob
readarray -t args < test.txt &&
for arg in ${args[@]}; do
cmd -- "$arg"
done
You can also do things like:
while IFS= read <&3 -r arg || [ -n "$arg" ]; do
[ -z "$arg" ] || cmd -- "$arg" 3<&-
done 3< test.txt
Which is standard sh
syntax.
-0
option toxargs
.-0
: When using this option you will need to ensure that the program which produces the input for xargs also uses a null character as a separator. Yourecho
andgrep
commands don't output a null character at the end of each line. (a null character is a distinct character, not the same thing as an empty string or partial string)