同时启动两个流程并从较早完成的流程中收集结果

时间:2019-03-23 13:16:19

标签: linux bash shell

假设我想运行两个命令c1c2,它们实际上在Linux上处理(但不修改)相同的数据。

现在我想同时启动它们,看看哪个更快完成,一旦一个进程完成,我将收集其输出(可以用c1 >> log1.txt转储到文件中),并终止另一个处理。

请注意,两个过程的处理时间可能会有很大差异,因此可以观察到,例如一个过程需要十秒钟,而另一个过程则需要60秒。

======================更新

我尝试了以下脚本集,但它在我的计算机上导致了无限循环:

import os


os.system("./launch.sh")

launch.sh

#!/usr/bin/env bash

rm /tmp/smack-checker2
mkfifo /tmp/smack-checker2

setsid bash -c "./sleep60.sh ; echo 1 > /tmp/run-checker2" &
pid0=$!
setsid bash -c "./sleep10.sh ; echo 2 > /tmp/run-checker2" &
pid1=$!

read line </tmp/smack-checker2
printf "Process %d finished earlier\n" "$line"
rm /tmp/smack-checker2
eval kill  -- -\$"pid$((line ^ 1))"

sleep60.sh

#!/usr/bin/env bash

sleep 60

sleep10.sh

#!/usr/bin/env bash

sleep 10

3 个答案:

答案 0 :(得分:2)

使用wait -n等待任一进程退出。忽略竞争条件和pid换行,

c1 & P1=$!
c2 & P2=$!
wait -n  # wait for either one to exit
if ! kill $P1; then
  # failure to kill $P1 indicates c1 finished first
  kill $P2
  # collect c1 results...
else
  # c2 finished first
  kill $P1
  # collect c2 results...
fi

有关文档,请参见help waitman bash

答案 1 :(得分:1)

此代码段可以给您一些想法吗?

#!/bin/sh

runproc1() {
  sleep 5
  touch proc1    # file created when terminated
  exit
}

runproc2() {
  sleep 10
  touch proc2    # file created when terminated
  exit
}

# remove flags
rm proc1
rm proc2

# run processes concurrently
runproc1 &
runproc2 &

# wait until one of them is finished
while [ ! -f proc1 -a ! -f proc2 ]; do
  sleep 1
  echo -n "."
done

这个想法是将两个进程封装成两个函数,最后,它们触摸文件以表示计算已终止。删除用作标志的文件后,这些功能将在后台执行。最后一步是观察两个文件是否出现。到那时,任何事情都可以做:继续等待其他进程,或者杀死它。

启动此精确脚本,大约需要5秒钟,然后终止。我看到创建了文件“ proc1”,没有proc2。几秒钟后(准确地说是5),还将创建“ proc2”。这意味着即使脚本终止,任何未完成的作业也会继续运行。

答案 2 :(得分:1)

我将运行2个进程,并使它们写入共享的命名管道 他们完成之后。从命名管道读取是一项阻止操作 因此您不需要在循环内使用有趣的sleep指令。它会 是:

#!/usr/bin/env bash

mkfifo /tmp/run-checker

(./sleep60.sh ; echo 0 > /tmp/run-checker) &
(./sleep10.sh ; echo 1 > /tmp/run-checker) &

read line </tmp/run-checker
printf "Process %d finished earlier\n" "$line"
rm /tmp/run-checker
kill -- -$$

sleep60.sh:

#!/usr/bin/env bash

sleep 60

sleep10.sh:

#!/usr/bin/env bash

sleep 10

编辑:

如果您要这样调用脚本形式的Python脚本:

#!/usr/bin/env python3

import os
os.system("./parallel.sh")
print("Done")

您将获得:

Process 1 finished earlier
./parallel.sh: line 11: kill: (-13807) - No such process
Done

这是因为kill -- -$$试图向过程发送TERM信号 man 1 kill中指定的组:

  

-n

     

其中n大于1。进程组n中的所有进程均为          发信号。当给出形式为'-n'的参数时          旨在表示一个过程组,或者信号必须是          首先指定,否则参数必须以“-”开头          选项,否则将被视为发送信号。

当您从终端运行parallel.sh时,它可以工作,因为$$是一个 子外壳以及过程组的PID。我用它是因为 杀死parallel.sh,process0或process1以及所有这些非常方便 他们的孩子一口气。但是,从以下位置调用parallel.sh时 Python脚本$$不再表示进程组,而kill -- 失败。

您可以像这样修改parallel.sh:

#!/usr/bin/env bash

mkfifo /tmp/run-checker

setsid bash -c "./sleep60.sh ; echo 0 > /tmp/run-checker" &
pid0=$!
setsid bash -c "./sleep10.sh ; echo 1 > /tmp/run-checker" &
pid1=$!

read line </tmp/run-checker
printf "Process %d finished earlier\n" "$line"
rm /tmp/run-checker
eval kill  -- -\$"pid$((line ^ 1))"

现在从Python脚本调用时,它也将起作用。最后一行

eval kill  -- -\$"pid$((line ^ 1))"

如果pid1更早完成,则杀死pid0;如果pid1更早完成,则杀死pid0 使用^二进制运算符将0转换为1,反之亦然。如果你 不喜欢它,您可以使用更详细的形式:

if [ "$line" -eq "$pid0" ]
then
    echo kill  "$pid1"
    kill  -- -"$pid1"
else
    echo kill  "$pid0"
    kill -- -"$pid0"
fi
相关问题