如何从bash脚本并行运行多个程序?

Bet*_*moo 218 parallel-processing bash

我正在尝试编写一个同时运行许多程序的.sh文件

我试过这个

prog1 
prog2
Run Code Online (Sandbox Code Playgroud)

但是运行prog1然后等到prog1结束然后开始prog2 ...

那我怎么能并行运行呢?

Ory*_*and 279

怎么样:

prog1 & prog2 && fg
Run Code Online (Sandbox Code Playgroud)

这将:

  1. 开始prog1.
  2. 将其发送到后台,但继续打印输出.
  3. 启动prog2,并将其保持在前台,以便您可以将其关闭ctrl-c.
  4. 当你关闭prog2,你会回到prog1前景,所以你也可以关闭它ctrl-c.

  • 试过这个,它对我没有预期的效果.然而,稍作改动:`prog1&prog2; fg`这是为了同时运行多个ssh隧道.希望这有助于某人. (17认同)
  • @ jnadro52一次终止两个进程的方法是`prog1&prog2 && kill $!`. (11认同)
  • 当`prog2`终止时,是否有一种简单的方法来终止`prog1`?想想`node srv.js&cucumberjs` (8认同)
  • 非常好,正是我想要的! (3认同)
  • 在SSH'ed shell上如果你执行这样的命令,杀死prog1将是棘手的.Ctrl-c对我不起作用.甚至杀死整个终端也让prog1继续运行. (3认同)
  • @ jnadro52你的解决方案的效果是,如果`prog2`无法立即运行,你将回到前台有'prog1`.如果这是可取的,那就没关系. (2认同)
  • 请注意,“fg”在 shell 脚本中不起作用。您将得到“fg:无作业控制”。这是因为脚本不是交互式 shell。 (2认同)

psm*_*ars 190

prog1 &
prog2 &
Run Code Online (Sandbox Code Playgroud)

  • 别忘了'等等'!是的,在bash中,您可以等待脚本的子进程. (44认同)
  • 另一种选择是使用`nohup`来防止程序在shell挂起时被杀死. (5认同)

Ole*_*nge 61

使用GNU Parallel http://www.gnu.org/software/parallel/,它很简单:

(echo prog1; echo prog2) | parallel
Run Code Online (Sandbox Code Playgroud)

或者如果您愿意:

parallel ::: prog1 prog2
Run Code Online (Sandbox Code Playgroud)

学到更多:

  • 值得注意的是,有不同版本的`parallel`具有不同的语法.例如,在Debian衍生物上,`moreutils`包中包含一个名为`parallel`的不同命令,它的行为完全不同. (4认同)
  • 是'并行'比使用`&`更好? (4认同)
  • @OptimusPrime 当作业数量多于核心数时,“并行”效果更好,在这种情况下,“&”将在每个核心上同时运行多个作业。(参见[鸽子洞原理](https://artofproblemsolving.com/wiki/index.php?title=Pigeonhole_Principle)) (3认同)
  • @OptimusPrime这真的取决于.GNU Parallel引入了一些开销,但作为回报,您可以更好地控制正在运行的作业和输出.如果两个作业同时打印,GNU Parallel将确保输出不混合. (2认同)
  • @OleTange“_你的命令行会喜欢你的。_”我也是。☺ (2认同)

tru*_*ktr 61

你可以使用wait:

some_command &
P1=$!
other_command &
P2=$!
wait $P1 $P2
Run Code Online (Sandbox Code Playgroud)

它将后台程序PID分配给变量($!是最后一个启动的进程'PID),然后wait命令等待它们.这很好,因为如果你杀死脚本,它也会杀死进程!

  • [根据我的经验](https://i.imgur.com/BpPhYaP.png),查杀等待也不会杀死其他进程. (4认同)
  • 使用“wait”无法终止我的第二个进程。 (2认同)

Qui*_*ant 33

如果你想能够轻松地运行并杀死多个进程ctrl-c,这是我最喜欢的方法:在(…)子shell中生成多个后台进程,并SIGINT执行trap kill 0,这将杀死子shell组中生成的所有内容:

(trap 'kill 0' SIGINT; prog1 & prog2 & prog3)
Run Code Online (Sandbox Code Playgroud)

你可以有复杂的流程执行结构,一切都将关闭单ctrl-c(只需确保最后的过程是在前台运行,即没有一个包括&prog1.3):

(trap 'kill 0' SIGINT; prog1.1 && prog1.2 & (prog2.1 | prog2.2 || prog2.3) & prog1.3)
Run Code Online (Sandbox Code Playgroud)

  • 这是迄今为止最好的答案。 (10认同)
  • `(trap 'kill 0' SIGINT; prog1 & prog2 & prog3 & wait)` 有助于确保所有程序完成 (5认同)
  • 什么是“杀0”?PID 0 是子shell 本身吗? (4认同)
  • @mpen 这是正确的, `kill` 程序将 `0` 解释为 *“当前进程组中的所有进程都已发出信号。”* [手册页](https://man7.org/linux/man-pages/man1 /kill.1.html#ARGUMENTS) 包含此描述。 (3认同)
  • 仅当最后一个命令(前台命令)也最后退出时,这才有效,否则整个命令退出得太早。 (2认同)
  • 这是一个很棒的答案,但我建议您首先移动更新的“wait”版本,并将其作为主要解决方案。99% 的情况下这是用户所期望的。 (2认同)

fer*_*min 9

#!/bin/bash
prog1 & 2> .errorprog1.log; prog2 & 2> .errorprog2.log
Run Code Online (Sandbox Code Playgroud)

将错误重定向到单独的日志.

  • 您必须在重定向后放入&符号并省略分号(&符号也将执行命令分隔符的功能):`prog1 2> .errorprog1.log&prog2 2> .errorprog2.log&` (13认同)
  • 它不起作用 - 错误不会重定向到文件.试试:`ls notthere1&2> .errorprog1.log; ls notthere2&2> .errorprog2.log`.错误转到控制台,两个错误文件都是空的.正如@Dennis Williamson所说,`&`是一个分隔符,就像```,所以(a)它需要在命令的最后(在任何重定向之后),以及(b)你不需要`; `at all :-) (2认同)

joe*_*joe 9

这对我来说效果很好(在这里找到):

sh -c 'command1 & command2 & command3 & wait'
Run Code Online (Sandbox Code Playgroud)

它输出混合的每个命令的所有日志(这就是我想要的),并且所有日志都用 ctrl+c 杀死。


3h4*_*h4x 8

有一个非常有用的程序叫nohup.

     nohup - run a command immune to hangups, with output to a non-tty
Run Code Online (Sandbox Code Playgroud)

  • `nohup`本身不会在后台运行任何东西,并且使用`nohup`不是在后台运行任务的必要条件或先决条件.它们通常在一起使用,但因此,这并不能回答这个问题. (2认同)

mkl*_*nt0 8

xargs -P <n>允许您<n>并行运行命令。

尽管这-P是一个非标准选项,但GNU(Linux)和macOS / BSD实施均支持该选项。

下面的例子:

  • 一次最多并行运行3个命令,
  • 仅在先前启动的进程终止时才启动其他命令。
time xargs -P 3 -I {} sh -c 'eval "$1"' - {} <<'EOF'
sleep 1; echo 1
sleep 2; echo 2
sleep 3; echo 3
echo 4
EOF
Run Code Online (Sandbox Code Playgroud)

输出看起来像:

1   # output from 1st command 
4   # output from *last* command, which started as soon as the count dropped below 3
2   # output from 2nd command
3   # output from 3rd command

real    0m3.012s
user    0m0.011s
sys 0m0.008s
Run Code Online (Sandbox Code Playgroud)

时间显示命令是并行运行的(最后一个命令仅在原始3个命令中的第一个终止后才启动,但执行速度非常快)。

xargs所有命令完成之前,命令本身不会返回,但是您可以在后台执行该操作&,方法是使用控制操作符终止该命令,然后使用wait内置函数等待整个xargs命令完成。

{
  xargs -P 3 -I {} sh -c 'eval "$1"' - {} <<'EOF'
sleep 1; echo 1
sleep 2; echo 2
sleep 3; echo 3
echo 4
EOF
} &

# Script execution continues here while `xargs` is running 
# in the background.
echo "Waiting for commands to finish..."

# Wait for `xargs` to finish, via special variable $!, which contains
# the PID of the most recently started background process.
wait $!
Run Code Online (Sandbox Code Playgroud)

注意:

  • BSD / MacOS的xargs要求指定命令的计数并行运行明确,而GNU xargs允许你指定-P 0要尽可能多的运行尽可能平行。

  • 并行运行的进程的输出在生成时会到达,因此将无法预料地交错

    • parallelOle的回答中所述(大多数平台都不标准),GNU可以方便地在每个进程的基础上对输出进行序列化(分组),并提供更多高级功能。


ljt*_*ljt 7

你可以尝试ppss.ppss相当强大 - 你甚至可以创建一个迷你集群.如果你有一批令人尴尬的并行处理,xargs -P也很有用.


Joa*_*iro 7

我最近有类似的情况,我需要同时运行多个程序,将它们的输出重定向到分离的日志文件并等待它们完成,我最终得到类似的东西:

#!/bin/bash

# Add the full path processes to run to the array
PROCESSES_TO_RUN=("/home/joao/Code/test/prog_1/prog1" \
                  "/home/joao/Code/test/prog_2/prog2")
# You can keep adding processes to the array...

for i in ${PROCESSES_TO_RUN[@]}; do
    ${i%/*}/./${i##*/} > ${i}.log 2>&1 &
    # ${i%/*} -> Get folder name until the /
    # ${i##*/} -> Get the filename after the /
done

# Wait for the processes to finish
wait
Run Code Online (Sandbox Code Playgroud)

资料来源:http://joaoperibeiro.com/execute-multiple-programs-and-redirect-their-outputs-linux/


arn*_*can 7

这是我使用的函数,以便并行运行max n进程(在示例中n = 4):

max_children=4

function parallel {
  local time1=$(date +"%H:%M:%S")
  local time2=""

  # for the sake of the example, I'm using $2 as a description, you may be interested in other description
  echo "starting $2 ($time1)..."
  "$@" && time2=$(date +"%H:%M:%S") && echo "finishing $2 ($time1 -- $time2)..." &

  local my_pid=$$
  local children=$(ps -eo ppid | grep -w $my_pid | wc -w)
  children=$((children-1))
  if [[ $children -ge $max_children ]]; then
    wait -n
  fi
}

parallel sleep 5
parallel sleep 6
parallel sleep 7
parallel sleep 8
parallel sleep 9
wait
Run Code Online (Sandbox Code Playgroud)

如果将max_children设置为内核数,则此函数将尝试避免空闲内核.

  • @EmmanuelDevaux:`wait -n` 需要 **`bash` 4.3+** 并将逻辑更改为等待指定/隐含进程的 _any_ 终止。 (3认同)