cat a.txt | xargs -I % echo %

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

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

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


当前回答

我有一个解决这个问题的好办法。 只要写一个命令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

其他回答

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.

这是另一种没有xargs和cat的方法:

while read stuff; do
  command1 "$stuff"
  command2 "$stuff"
  ...
done < a.txt

我目前的BKM是

... | xargs -n1 -I % perl -e 'system("echo 1 %"); system("echo 2 %");'

不幸的是,这使用perl,它不太可能安装比bash;但是它处理的输入比接受的答案要多。(我欢迎不依赖perl的普遍版本。)

@KeithThompson的建议

 ... | xargs -I % sh -c 'command1; command2; ...'

很好-除非您的输入中有shell注释字符#,在这种情况下,第一个命令的部分和第二个命令的全部将被截断。

如果输入来自文件系统列表,例如ls或find,并且您的编辑器创建了名称为#的临时文件,则哈希值#可能非常常见。

问题示例:

$ bash 1366 $>  /bin/ls | cat
#Makefile#
#README#
Makefile
README

哎呀,问题来了:

$ bash 1367 $>  ls | xargs -n1 -I % sh -i -c 'echo 1 %; echo 2 %'
1
1
1
1 Makefile
2 Makefile
1 README
2 README

啊,这样好多了:

$ bash 1368 $>  ls | xargs -n1 -I % perl -e 'system("echo 1 %"); system("echo 2 %");'
1 #Makefile#
2 #Makefile#
1 #README#
2 #README#
1 Makefile
2 Makefile
1 README
2 README
$ bash 1369 $>  

试试这个:

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

我做的一件事是添加到.bashrc/。配置此功能:

function each() {
    while read line; do
        for f in "$@"; do
            $f $line
        done
    done
}

然后你就可以做

... | each command1 command2 "command3 has spaces"

它比xargs或-exec更简洁。如果还需要这种行为,还可以修改该函数,将读取的值插入到每个命令中的任意位置。