通过xargs调用多个命令

时间:2011-08-05 15:22:15

标签: bash xargs

cat a.txt | xargs -I % echo %

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

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

但是xargs不接受这种形式。我知道的一个解决方案是我可以定义一个函数来包装命令,但它不是一个管道,我不喜欢它。还有其他解决方案吗?

12 个答案:

答案 0 :(得分:379)

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

请注意,这是Useless Use Of cat。我把它写成:

< a.txt xargs -I % sh -c 'command1; command2; ...'

(是的,重定向可以在命令的开头。)

大概command1和/或command2将包含一个或多个%个字符;否则,对-I %的{​​{1}}选项没有多大意义。

答案 1 :(得分:31)

使用GNU Parallel,您可以:

cat a.txt | parallel 'command1 {}; command2 {}; ...; '

观看介绍视频以了解详情:https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

答案 2 :(得分:23)

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

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

答案 3 :(得分:16)

您可以使用

cat file.txt | xargs -i  sh -c 'command {} | command2 {} && command3 {}'

{} =文本文件中每一行的变量

答案 4 :(得分:14)

我做的一件事是将.bashrc / .profile添加到此函数中:

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

那么你可以做像

这样的事情
... | each command1 command2 "command3 has spaces"

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

答案 5 :(得分:9)

派对有点晚了。

我使用下面的格式在迁移之前使用数千个小文件压缩我的目录。如果你在命令中不需要单引号,它应该可以工作。

经过一些修改后,我确信它对某人有用。在Cygwin(babun)

中测试过
find . -maxdepth 1 ! -path . -type d -print0 | xargs -0 -I @@ bash -c '{ tar caf "@@.tar.lzop" "@@" && echo Completed compressing directory "@@" ; }'

find .在这里找到 -maxdepth 1不要进入子目录
! -path .排除。 /当前目录路径
-type d仅匹配目录
-print0以null字节分隔输出\ 0
| xargs管道到xargs
-0输入为空分隔字节
-I @@占位符是@@。用输入替换@@ bash -c '...'运行Bash命令
{...}命令分组
&&仅在上一个命令成功退出(退出0)

时执行下一个命令

最终;很重要,否则会失败。

输出:

Completed compressing directory ./Directory1 with meta characters in it
Completed compressing directory ./Directory2 with meta characters in it
Completed compressing directory ./Directory3 with meta characters in it

2018年7月更新:

如果你喜欢黑客并玩耍,这里有一些有趣的东西:

echo "a b c" > a.txt
echo "123" >> a.txt
echo "###this is a comment" >> a.txt
cat a.txt
myCommandWithDifferentQuotes=$(cat <<'EOF'                                     
echo "command 1: $@"; echo 'will you do the fandango?'; echo "command 2: $@"; echo
EOF
)
< a.txt xargs -I @@ bash -c "$myCommandWithDifferentQuotes" -- @@

输出:

command 1: a b c
will you do the fandango?
command 2: a b c

command 1: 123
will you do the fandango?
command 2: 123

command 1: ###this is a comment
will you do the fandango?
command 2: ###this is a comment

说明:
- 创建单个班轮脚本并将其存储在变量中 - xargs读取a.txt并将其作为bash脚本执行 - @@确保每次传递整行 - @@之后放置--,确保@@被视为bash命令的位置参数输入,而不是bash start { {1}},例如OPTION本身就意味着-c

run command是神奇的,它适用于许多其他内容,即--,甚至ssh

答案 6 :(得分:9)

我更喜欢允许干运行模式的风格(没有| sh):

cat a.txt | xargs -I % echo "command1; command2; ... " | sh

也适用于管道:

cat a.txt | xargs -I % echo "echo % | cat " | sh

答案 7 :(得分:7)

对我有用的另一种可能的解决方案是 -

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

答案 8 :(得分:3)

这似乎是最安全的版本。

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

({-0可以被删除,tr可以用重定向替换(或者该文件可以用空分隔的文件替换)。它主要在此,因为我主要使用{{1 }},输出为xargs,输出为find(这可能与没有扩展名-print0的{​​{1}}版本有关)

这是安全的,因为args在执行时会将参数作为数组传递给Shell。当使用xargs

获得所有外壳程序时,外壳程序(至少-0)将它们作为未更改的数组传递给其他进程。

如果使用bash,则如果字符串包含双引号,分配将失败。对于使用["$@"][1]...| xargs -r0 -I{} bash -c 'f="{}"; command "$f";' ''的每个变体来说都是如此。 (由于将其替换为字符串,您始终可以通过在输入数据中插入意外字符(例如引号,反引号或美元符号)来注入命令)

如果这些命令一次只能使用一个参数:

-i

或具有较少的过程:

-I

如果您具有GNU 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;' '' 扩展名的服务器,并且希望并行运行32个进程,每个进程的每个命令的参数不得超过10个:

xargs

对于输入中的任何特殊字符,这应该是可靠的。 (如果输入为null分隔。)如果某些行包含换行符,则-P版本将获得一些无效的输入,但是用换行符分隔的文件不可避免。

tr '[\n]' '[\0]' < a.txt | xargs -r0 -n10 -P32 /bin/bash -c 'command1 "$@"; command2 "$@";' '' 的第一个空白参数是由于以下原因:(来自bash man page)(感谢@clacke)

tr

答案 9 :(得分:1)

我目前的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 $>  

答案 10 :(得分:0)

尝试一下:

select_data = """SELECT name, details;
             FROM {} WHERE b_date >= date('2020-09-14')
             AND b_date < date('2020-09-14')
             AND b_date IS NULL 
             + INTERVAL 1 DAY""".format(table)

它并行运行十个线程,并对文件夹结构中的所有存储库执行您想要的git命令。无论回购是深一层还是n层。

例如: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'

答案 11 :(得分:0)

我有解决问题的好主意。 只写一个命令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