并行运行可指定数量的命令 - 对比xargs -P,GNU parallel和“moreutils”parallel

时间:2017-03-07 14:57:37

标签: bash parallel-processing xargs gnu-parallel

我正在尝试在bash脚本中的26台服务器上运行多个mongodump。

我可以运行3个命令,如

mongodump -h staging .... & mongodump -h production .... & mongodump -h web ... &

同时,当一个人完成时我想开始另一个mongodump。

我无法同时运行所有26个mongodumps命令,服务器将在CPU上运行。同时最多3个mongodumps。

2 个答案:

答案 0 :(得分:5)

您可以使用 xarg -P选项并行运行可指定数量的调用

请注意-P选项为not mandated by POSIX,但GNU xargs和BSD / macOS xargs都支持它。

xargs -P 3 -n 1 mongodump -h <<<'staging production web more stuff and so on'

这会并行运行mongodump -h stagingmongodump -h productionmongodump -h web,等待所有3次调用完成,然后继续mongodump -h moremongodump -h stuffmongodump -h and,等等。

-n 1从输入流中获取单个参数并调用mongodump;如有必要,根据需要调整输入中的单引号或双引号。

注意: GNU xargs - 但不是BSD xargs - 支持-P 0,其中0表示:&#34;尽可能多地运行尽可能同时处理。&#34;

默认情况下,通过stdin提供的参数追加到指定的命令 如果您需要控制 where ,则在结果命令中放置相应的参数,

  • 逐行提供参数
  • 使用-I {}表示,并将{}定义为每个输入行的占位符。
xargs -P 3 -I {} mongodump -h {} after <<<$'staging\nproduction\nweb\nmore\nstuff'

现在每个输入参数都替换为{},允许参数after到来。

但请注意,每个输入行总是作为单个参数传递。

BSD / macOS xargs允许您将-n-J {}结合使用,而无需提供基于行的输入,但GNU xargs没有&#39} ; t支持-J 简而言之:只有BSD / macOS允许您将输入参数的放置与一次读取多个参数结合起来。

请注意,xargs并行序列化stdout输出,以便并行进程的输出 interleaved
使用 GNU parallel来避免此问题 - 请参阅下文。

替代方案:parallel

xargs具有成为标准实用程序的优势,因此在支持-P的平台上,没有先决条件。

在Linux世界中(虽然也通过Homebrew在macOS上),有两个专门用于并行运行命令的实用程序,不幸的是,它们共享相同的名称; 通常,您必须按需安装

    来自parallel包的
  • moreutils(二进制) - 请参阅its home page

  • - 更强大 - 来自parallel的GNU parallel(Perl脚本)谢谢,twalberg - 请参阅{{ 3}}

如果您已有parallel实用程序,parallel --version将告诉您它是哪一个(GNU parallel报告版本号和版权信息,&#34; moreutils&#34; parallel抱怨无效选项并显示语法摘要。)

使用&#34; moreutils&#34; parallel

parallel -j 3 -n 1 mongodump -h -- staging production web more stuff and so on

# Using -i to control placement of the argument, via {}
# Only *1* argument at at time supported in that case.
parallel -j 3 -i mongodump -h {} after -- staging production web more stuff and so on

xargs不同,此parallel实现并未将参数从 stdin 传递出去;必须在--之后在命令行上传递所有传递参数。

据我所知,此parallel实施提供的唯一功能超出了xargs所能提供的功能:

  • -l选项允许延迟进一步的调用,直到系统加载超额低于指定的阈值。
  • 可能这(来自man页面):&#34; stdout和stderr通过相应的内部管道进行序列化,以防止令人讨厌的并发输出行为。&#34;尽管我&#39;我们在man页面的日期为2009-07-2的版本中发现了 not 的情况 - 请参阅上一节。

使用GNU parallel

its home page寻求帮助的提示。

parallel -P 3 -n 1 mongodump -h <<<$'staging\nproduction\nweb\nmore\nstuff\nand\nso\non'

# Alternative, using ::: followed by the target-command arguments.
parallel -P 3 -n 1 mongodump -h ::: staging production web more stuff and so on 

# Using -n 1 and {} to control placement of the argument.
# Note that using -N rather than -n would allow per-argument placement control
# with {1}, {2}, ...
parallel -P 3 -n 1 mongodump -h {} after <<<$'staging\nproduction\nweb\nmore\nstuff\nand'
  • xargs一样,传递参数是通过 stdin 提供的,但GNU parallel也支持在可配置的分隔符之后将它们放在命令行上(默认为:::)。

  • xargs不同,每个输入都被视为一个参数。

  • 警告:如果您的命令涉及引用字符串,则必须使用-q将它们作为不同的参数传递;例如,parallel -q sh -c 'echo hi, $0' ::: there仅适用于-q

  • GNU xargs一样,您可以使用-P 0一次运行尽可能多的调用 ,充分利用机器人的能力,意味着,根据Ole,&#34;直到GNU Parallel遇到限制(文件句柄和进程)&#34;。

    • 方便的是,省略 -P并不像其他公用事业那样一次只运行一个进程,而是运行一个进程每CPU核心
  • 默认情况下,并行执行的命令输出会按进程自动序列化(分组),以避免交错输出。

    • 这通常是可取的,但请注意,这意味着您只能开始看到其他命令&#39;输出第一个已创建输出的输出已终止
    • 使用选项--line-buffer--lb在更新的版本中)选择退出此行为或
      -u--ungroup)允许甚至单个输出行混合来自不同进程的输出;有关详细信息,请参阅手册。

GNU parallel,旨在成为xargs的更好继承者,提供了更多功能:一个值得注意的例子是能够Ole Tange,可选择基于Perl 正则表达式;另请参阅:man parallelman parallel_tutorial

可选读取:测试输出序列化行为

以下命令测试xargs和两个parallel实现如何处理并行运行的命令的交错输出 - 无论它们是在到达时显示输出还是尝试序列化它:

2级序列化,两者都引入了开销:

  • 行级序列化:防止来自不同进程的部分行在单个输出行上混合。

  • 流程级序列化:确保将给定流程的所有输出行组合在一起 这是用户最友好的方法,但请注意,这意味着您只能开始看到其他命令&#39;输出(按顺序)一旦创建输出的第一个终止

据我所知,只有GNU parallel提供任何序列化(尽管&#34; moreutils&#34; parallel 2009-07-2的手册页说 [1] ),支持两种方法。

以下命令假设存在可执行脚本./tst,其中包含以下内容:

#!/usr/bin/env bash

printf "$$: [1/2] entering with arg(s): $*"
sleep $(( $RANDOM / 16384 ))
printf " $$: [2/2] finished entering\n"
echo "  $$: stderr line" >&2
echo "$$: stdout line"
sleep $(( $RANDOM / 8192 ))
echo "    $$: exiting"

xargs (在Ubuntu 16.04和macOS 10.12上都有GNU和BSD / macOS实现):

不会发生序列化:单个输出行可以包含多个进程的输出。

$ xargs -P 3 -n 1 ./tst <<<'one two three'
2593: [1/2] entering with arg(s): one2594: [1/2] entering with arg(s): two 2593: [2/2] finished entering
  2593: stderr line
2593: stdout line
2596: [1/2] entering with arg(s): three   2593: exiting
 2594: [2/2] finished entering
  2594: stderr line
2594: stdout line
 2596: [2/2] finished entering
  2596: stderr line
2596: stdout line
   2594: exiting
   2596: exiting

<强>&#34; moreutils&#34; parallelman页面日期为2009-07-02的版本)

不会发生序列化:单个输出行可以包含多个进程的输出。

$ parallel -j 3 ./tst -- one two three
3940: [1/2] entering with arg(s): one3941: [1/2] entering with arg(s): two3942: [1/2] entering with arg(s): three 3941: [2/2] finished entering
  3941: stderr line
3941: stdout line
 3942: [2/2] finished entering
  3942: stderr line
3942: stdout line
 3940: [2/2] finished entering
  3940: stderr line
3940: stdout line
   3941: exiting
   3942: exiting

GNU parallel (版本20170122)

默认情况下会发生进程级序列化(分组)。 使用--line-buffer(在较新版本中为--lb)来选择行级序列化,或使用-u选择退出任何类型的序列化 (--ungroup)。

请注意,在每个组中,stderr输出在标准输出后输出(而版本20170122附带的手册页声称stderr输出首先)。

$ parallel -P 3 ./tst ::: one two three
2544: [1/2] entering with arg(s): one 2544: [2/2] finished entering
2544: stdout line
   2544: exiting
  2544: stderr line
2549: [1/2] entering with arg(s): three 2549: [2/2] finished entering
2549: stdout line
   2549: exiting
  2549: stderr line
2546: [1/2] entering with arg(s): two 2546: [2/2] finished entering
2546: stdout line
   2546: exiting
  2546: stderr line

[1]&#34; stdout和stderr通过相应的内部管道进行序列化,以防止烦人的并发输出行为。&#34;
请告诉我,我是否遗漏了某些东西。

答案 1 :(得分:1)

如果您只是排除每隔3 &(如果它只在一行上使用;),那么它就不会并行执行整个事情。

例如:

echo "Hello" & sleep 1 ;
echo "Hello Again" & sleep 1 ;
echo "Once More" & sleep 1 ;