cat a.txt | xargs -I % echo %

在上面的例子中,xargs使用echo %作为命令参数。但在某些情况下,我需要多个命令来处理参数,而不是一个。例如:

cat a.txt | xargs -I % {command1; command2; ... }

但是xargs不接受这种形式。我知道的一种解决方案是,我可以定义一个函数来包装命令,但我想避免这样做,因为它很复杂。有没有更好的解决方案?


当前回答

cat a.txt | xargs -d $'\n' sh -c 'for arg do command1 "$arg"; command2 "$arg"; ...; done' _

...或者,不用无用地使用cat:

<a.txt xargs -d $'\n' sh -c 'for arg do command1 "$arg"; command2 "$arg"; ...; done' _

来解释一些细节:

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.

其他回答

cat a.txt | xargs -d $'\n' sh -c 'for arg do command1 "$arg"; command2 "$arg"; ...; done' _

...或者,不用无用地使用cat:

<a.txt xargs -d $'\n' sh -c 'for arg do command1 "$arg"; command2 "$arg"; ...; done' _

来解释一些细节:

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.

这似乎是最安全的版本。

tr '[\n]' '[\0]' < a.txt | xargs -r0 /bin/bash -c 'command1 "$@"; command2 "$@";' ''

(可以删除-0,并将tr替换为重定向(或者可以将文件替换为空分隔文件)。它主要是在那里,因为我主要使用xargs查找与-print0输出)(这可能也与xargs版本没有-0扩展名有关)

这是安全的,因为args在执行时将参数作为数组传递给shell。当使用["$@"][1]获得所有这些进程时,shell(至少是bash)会将它们作为未更改的数组传递给其他进程

如果你用…| xargs -r0 -I{} bash -c 'f="{}";命令"$f";' ",如果字符串包含双引号,则赋值将失败。这对于每个使用-i或-i的变体都是正确的。(由于它被替换为字符串,您总是可以通过在输入数据中插入意外字符(如引号、反引号或美元符号)来注入命令)

如果命令一次只能接受一个参数:

tr '[\n]' '[\0]' < a.txt | xargs -r0 -n1 /bin/bash -c 'command1 "$@"; command2 "$@";' ''

或者用更少的进程:

tr '[\n]' '[\0]' < a.txt | xargs -r0 /bin/bash -c 'for f in "$@"; do command1 "$f"; command2 "$f"; done;' ''

如果您有GNU xargs或其他带有-P扩展名的文件,并且希望并行运行32个进程,每个进程每个命令的参数不超过10个:

tr '[\n]' '[\0]' < a.txt | xargs -r0 -n10 -P32 /bin/bash -c 'command1 "$@"; command2 "$@";' ''

这对于输入中的任何特殊字符都应该是健壮的。(如果输入是空分隔的。)如果某些行包含换行符,tr版本将得到一些无效的输入,但对于换行符分隔的文件,这是不可避免的。

bash -c的第一个空白参数是由于:(来自bash手册页)(感谢@clacke)

-c   If the -c option is present, then  commands  are  read  from  the  first  non-option  argument  com‐
     mand_string.   If there are arguments after the command_string, the first argument is assigned to $0
     and any remaining arguments are assigned to the positional parameters.  The assignment  to  $0  sets
     the name of the shell, which is used in warning and error messages.

我有一个解决这个问题的好办法。 只要写一个命令mcmd,就可以了

find . -type f | xargs -i mcmd echo {} @@ cat {} @pipe sed -n '1,3p'

mcmd的内容如下:

echo $* | sed -e 's/@@/\n/g' -e 's/@pipe/|/g' | csh

试试这个:

git config --global alias.all '!f() { find . -d -name ".git" | sed s/\\/\.git//g | xargs -P10 -I{} git --git-dir={}/.git --work-tree={} $1; }; f'

它并行运行10个线程,并执行任何你想要在文件夹结构中全部回购的git命令。无论回购是1层还是n层。

例句:get all pull

对我来说另一个可行的解决方案是——

cat a.txt | xargs bash -c 'command1 $@; command2 $@' bash

注意结尾的“bash”-我假设它作为argv[0]传递给bash。在这种语法中,如果没有它,每个命令的第一个参数将丢失。它可能是任何一个词。

例子:

cat a.txt | xargs -n 5 bash -c 'echo -n `date +%Y%m%d-%H%M%S:` ; echo " data: " $@; echo "data again: " $@' bash