cat a.txt | xargs -I % echo %
In the example above, xargs takes echo %
as the command argument. But in some cases, I need multiple commands to process the argument instead of one. For example:
cat a.txt | xargs -I % {command1; command2; ... }
But xargs doesn't accept this form. One solution I know is that I can define a function to wrap the commands, but it's not a pipeline, I don't prefer it. Is there another solution?
cat a.txt | xargs -d $'\n' sh -c 'for arg do command1 "$arg"; command2 "$arg"; ...; done' _
...or, without a Useless Use Of cat:
<a.txt xargs -d $'\n' sh -c 'for arg do command1 "$arg"; command2 "$arg"; ...; done' _
To explain some of the finer points:
The use of "$arg"
instead of %
(and the absence of -I
in the xargs
command line) is for security reasons: Passing data on sh
's command-line argument list instead of substituting it into code prevents content that data might contain (such as $(rm -rf ~)
, to take a particularly malicious example) from being executed as code.
Similarly, the use of -d $'\n'
is a GNU extension which causes xargs
to treat each line of the input file as a separate data item. Either this or -0
(which expects NULs instead of newlines) is necessary to prevent xargs from trying to apply shell-like (but not quite shell-compatible) parsing to the stream it reads. (If you don't have GNU xargs, you can use tr '\n' '\0' <a.txt | xargs -0 ...
to get line-oriented reading without -d
).
The _
is a placeholder for $0
, such that other data values added by xargs
become $1
and onward, which happens to be the default set of values a for
loop iterates over.