通过xargs调用多个命令

Dag*_*ang 286 bash xargs

cat a.txt | xargs -I % echo %
Run Code Online (Sandbox Code Playgroud)

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

cat a.txt | xargs -I % {command1; command2; ... }
Run Code Online (Sandbox Code Playgroud)

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

Kei*_*son 414

cat a.txt | xargs -d $'\n' sh -c 'for arg do command1 "$arg"; command2 "$arg"; ...; done' _
Run Code Online (Sandbox Code Playgroud)

请注意,这是一种无用的猫.我把它写成:

<a.txt xargs -d $'\n' sh -c 'for arg do command1 "$arg"; command2 "$arg"; ...; done' _
Run Code Online (Sandbox Code Playgroud)

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

据推测"$arg"和/或%将包含一个或多个-I字符; 否则就不会有太大指向xargs选项sh.

  • 对于那些不熟悉`sh -c`的人 - 请注意每个命令后面的分号不是可选的,即使它是列表中的最后一个命令. (56认同)
  • 为了澄清上面关于分号的注释,在结束`}之前需要一个分号:`sh -c'{command1; 命令2; }' - 但是在命令序列的末尾不需要使用大括号:`sh -c'command1; command2'` (23认同)
  • 至少在我的配置中,在初始"{"之后必须有一个空格.在结束大括号之前不需要空间,但正如Sussman先生指出的那样,你需要一个结束分号. (6认同)
  • 如果你将字符串中的'%`字符包含在传递给`sh -c`的字符串中,那么就容易出现安全漏洞:包含`$(rm -rf~)'$(rm -rf~)'的文件名`(这是在普通UNIX文件系统上的文件名中具有完全合法的子字符串!)将导致某人**非常糟糕的一天. (6认同)
  • 这个答案以前在`command1`和`command2`周围有大括号; 我后来意识到他们没有必要. (4认同)
  • @ToddWalton:是的,通常重定向可以出现在命令的任何地方.例如,`</ etc/motd cat -n`,`cat </ etc/motd -n`和`cat -n </ etc/motd`都是等价的.(至少对于bash来说,但对于大多数炮弹来说似乎都是如此.) (2认同)

Ole*_*nge 34

使用GNU Parallel,您可以:

cat a.txt | parallel 'command1 {}; command2 {}; ...; '
Run Code Online (Sandbox Code Playgroud)

观看介绍视频以了解更多信息:https://www.youtube.com/playlist?list = PL284C9FF2488BC6D1

  • 通过从未知站点运行随机脚本来安装工具是一种可怕的做法.对于流行的发行版,Parallel有一些foriciall包,它可以比随机wget | sh更值得信任(在某种程度上)... (52认同)
  • 在实践中,我不知道pi.dk属于作者.实际上验证这是这种情况,考虑如何在wget中使用ssl并检查此命令是否完成它应该做的事情是一些工作.您认为官方软件包可能包含恶意代码的观点是正确的,但这也适用于wget软件包. (9认同)
  • 让我们看看什么是最简单的攻击向量:Pi.dk由GNU Parallel的作者控制,所以要攻击你必须闯入服务器或接管DNS.要接管分发的官方包,您通常可以自愿维护包.因此,虽然你可能是正确的,但在这种特殊情况下,你的评论似乎是不合理的. (4认同)
  • 如果OP要执行的每个命令必须是顺序的,那么这可能不是最佳解决方案吗? (3认同)
  • @IcarianComplex添加-j1将解决这个问题. (2认同)
  • “包的作者控制着域名”,直到域名无法续订,并被利用现成攻击媒介的敌对行为者占领。或者直到有人破坏他的网站并添加恶意代码。或者直到有人意识到,尽管生成了 3 个不同的校验和,但没有任何操作(如所写)阻止运行 install.sh,即使和不匹配。即使总和不匹配,读者也可能会得出这样的结论:由于该帖子是 10 多年前发布的,因此 shell 脚本已更改,并且此处未发布新的校验和。 (2认同)

hmo*_*liu 24

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

while read stuff; do
  command1 "$stuff"
  command2 "$stuff"
  ...
done < a.txt
Run Code Online (Sandbox Code Playgroud)

  • 这效果非常好。您还可以将其用作管道命令,例如“$ command | 读取行时;执行 c1 $line; c2 $行;完成` (3认同)
  • 越野车,如给定的.除非你清除'IFS`,否则它将忽略文件名中的前导和尾随空格; 除非你添加`-r`,带有文字反斜杠的文件名将忽略这些字符. (2认同)

小智 21

您可以使用

cat file.txt | xargs -i  sh -c 'command {} | command2 {} && command3 {}'
Run Code Online (Sandbox Code Playgroud)

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

  • 这对我来说效果很好,幸运的是没有一个 zoneinfo 时区定义包含 rm -rf ;) (10认同)
  • +1。令人难以置信的是,人们在不需要的地方(例如处理 IP 地址、PID 或 USB 设备名称列表)在安全性上花费了多少精力 (9认同)
  • 然而,作为一个“通用解决方案”,安全问题*应该*被正确地注意到。除非您了解风险,否则不应在不受信任或未经消毒的输入上使用该命令。如果你确实相信你的输入,那就继续吧。 (8认同)
  • 这是不安全的.如果你的`file.txt`包含一个带有'$(rm -rf~)`作为子串的数据怎么办? (6认同)
  • mac上的错误:xargs:非法选项 - i (3认同)

小智 15

我做的一件事是添加.bashrc/.profile这个函数:

function each() {
    while read line; do
        for f in "$@"; do
            $f $line
        done
    done
}
Run Code Online (Sandbox Code Playgroud)

然后你可以做的事情

... | each command1 command2 "command3 has spaces"
Run Code Online (Sandbox Code Playgroud)

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


sdk*_*kks 12

派对有点晚了.

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

经过一些修改,我确信它对某人有用.测试Cygwin(babun)

find . -maxdepth 1 ! -path . -type d -print0 | xargs -0 -I @@ bash -c '{ tar caf "@@.tar.lzop" "@@" && echo Completed compressing directory "@@" ; }'
Run Code Online (Sandbox Code Playgroud)

find .在此处查找
-maxdepth 1不要进入子目录
! -path .排除./当前目录路径
-type d仅匹配目录
-print0以空字节分隔输出\ 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
Run Code Online (Sandbox Code Playgroud)

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" -- @@
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)

说明:
-创建一个单一的衬垫脚本,并将其存储在一个变量
- xargs读取a.txt并执行它作为bash脚本
- @@确保整条生产线是通过每一次
-@@--确保@@被视为位置参数输入bash命令,而不是一个bash开始OPTION,即喜欢-c自己的意思run command

--是神奇的,它适用于许多其他的东西,即ssh,甚至kubectl


bra*_*blc 11

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

cat a.txt | xargs -I % echo "command1; command2; ... " | sh
Run Code Online (Sandbox Code Playgroud)

也适用于管道:

cat a.txt | xargs -I % echo "echo % | cat " | sh
Run Code Online (Sandbox Code Playgroud)

  • 这将起作用,直到您要使用GNU xargs的-P选项为止(否则,我通常在find上使用-exec,因为我的输入主要是文件名) (2认同)

小智 7

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

cat a.txt | xargs bash -c 'command1 $@; command2 $@' bash
Run Code Online (Sandbox Code Playgroud)

注意最后的'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
Run Code Online (Sandbox Code Playgroud)

  • 如果你不引用`"$ @"`,那么你就是字符串拆分和全局扩展参数列表. (4认同)

Ger*_*erg 7

这似乎是最安全的版本。

tr '[\n]' '[\0]' < a.txt | xargs -r0 /bin/bash -c 'command1 "$@"; command2 "$@";' ''
Run Code Online (Sandbox Code Playgroud)

-0可以删除并tr用重定向替换(或该文件可以用空分隔的文件代替)。它主要在此,因为我主要xargsfindwith -print0输出配合使用)(这也可能与xargs没有-0扩展名的版本有关)

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

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

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

tr '[\n]' '[\0]' < a.txt | xargs -r0 -n1 /bin/bash -c 'command1 "$@"; command2 "$@";' ''
Run Code Online (Sandbox Code Playgroud)

或使用较少的流程:

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

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

tr '[\n]' '[\0]' < a.txt | xargs -r0 -n10 -P32 /bin/bash -c 'command1 "$@"; command2 "$@";' ''
Run Code Online (Sandbox Code Playgroud)

对于输入中的任何特殊字符,这应该是可靠的。(如果输入为null分隔。)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.
Run Code Online (Sandbox Code Playgroud)

  • 这与xargs无关。bash和-c首先(在命令之后)接受一个将作为进程名称的参数,然后接受位置参数。尝试`bash -c'echo“ $ @”'1 2 3 4`看看结果如何。 (3认同)
  • 现在,这是唯一完整且正确的答案。 (3认同)