Say I have a list/array:
list=(a b c)
how can I echo each element to xargs? Something like:
for v in list; do echo v; done; | xargs
is there a less verbose way?
Say I have a list/array:
list=(a b c)
how can I echo each element to xargs? Something like:
for v in list; do echo v; done; | xargs
is there a less verbose way?
printf '%s\n' "${list[@]}" | xargs
This would print each element of list
on its own line and that newline-delimited list would be passed to xargs
.
"${list[@]}"
would expand to the individually double quoted elements of list
. printf
will reuse its formatting string if given more arguments than there are placeholders in the formatting string.
That only works however if the elements don't contain whitespace nor single quotes nor double quotes nor backslash characters, are not the empty string (and with some xargs
implementations are only made of text and are not _
and are relatively short).
For arbitrary (not too long¹) elements (that don't contain NUL characters, but current versions of bash can't store NULs in their variables anyway), you could pass the list as NUL-delimited records as supported by most xargs
implementations with the -0
option (will be in the next version of the POSIX standard):
{
[ "${#list[@]}" -eq 0 ] || printf '%s\0' "${list[@]}"
} | xargs
Using a print0
helper function may help:
print0() {
[ "$#" -eq 0 ] || printf '%s\0' "$@"
}
print0 "${list[@]}" | xargs -0
Note that for the empty list, except on NetBSD, the command (echo
by default) will still be invoked once without argument. That can be avoided with the -r
option (also in the next version of the POSIX standard).
¹ while xargs
is meant to work around the limit of the execve()
system call, it won't cut single arguments in the middle of them, so if a single argument is larger than that limit, execve()
won't be able to execute the command. Linux also has a limit on the size of individual arguments, separate from the overall limit on the cumulative size of all the arguments and environment variables that is common to most systems.
-0
for xargs
.
– Kusalananda
Dec 27 '18 at 11:40
Unless it's a particularly long list you may be able to dispense with xargs
entirely
list=(a b c)
With xargs
printf "%s\n" "${list[@]}" | xargs foo # Results in « foo a b c »
Without xargs
foo "${list[@]}" # Also results in « foo a b c »
Of course, the applicability of this to your situation is dependent on the additional flags (if any) that you want to pass to your actual xargs
command.
If you want a foo
to test this with, run this script and replace all instances above of foo
with ./foo
:
cat <<'EOF' > foo
#!/bin/bash
echo "Got $# arg(s): $*"
for ARG in "$@"; do echo "> $ARG <"; done
EOF
chmod a+x foo
Note also that by default (and as shown here), xargs
splits its arguments on whitespace, so:
list=('a b' c d) # Three arguments; the first contains a space
printf "%s\n" "${list[@]}" | xargs foo # Gives foo four arguments « a b c d »
foo "${list[@]}" # Gives foo three arguments, with the first containing a space, « 'a b' c d »