给定两个后台命令,当其中一个退出时,终止其余命令


14

我有一个启动两个服务器的简单bash脚本:

#!/bin/bash
(cd ./frontend && gulp serve) & (cd ./backend && gulp serve --verbose)

如果第二个命令退出,则似乎第一个命令继续运行。

我如何更改此设置,以便如果其中一个命令退出,另一个命令终止?

注意,我们不需要检查后台进程的错误级别,只需检查它们是否已经退出即可。


为什么不gulp ./fronend/serve && gulp ./backend/serve --verbose呢?
heemayl 2015年

serve是参数,而不是文件,因此需要设置当前目录。
blah238 2015年

1
另外,这些是长时间运行的进程,需要同时运行,如果不清楚,请抱歉。
blah238 2015年

Answers:


22

这将启动两个进程,等待第一个进程完成,然后杀死另一个进程:

#!/bin/bash
{ cd ./frontend && gulp serve; } &
{ cd ./backend && gulp serve --verbose; } &
wait -n
pkill -P $$

怎么运行的

  1. 开始:

    { cd ./frontend && gulp serve; } &
    { cd ./backend && gulp serve --verbose; } &

    以上两个命令在后台启动两个进程。

  2. 等待

    wait -n

    这等待任一后台作业终止。

    由于存在该-n选项,因此需要bash 4.3或更高版本。

  3. pkill -P $$

    这将杀死当前进程为其父进程的任何作业。换句话说,这将杀死仍在运行的任何后台进程。

    如果您的系统没有pkill,请尝试使用以下命令替换此行:

    kill 0

    这也杀死了当前的流程组

易于测试的例子

通过更改脚本,即使没有gulp安装,我们也可以对其进行测试:

$ cat script.sh 
#!/bin/bash
{ sleep $1; echo one;  } &
{ sleep $2; echo two;  } &
wait -n
pkill -P $$
echo done

上面的脚本可以按那样运行,bash script.sh 1 3并且第一个进程首先终止。或者,可以按原样运行它,bash script.sh 3 1第二个过程将首先终止。无论哪种情况,都可以看到它按预期运行。


这看起来很棒。不幸的是,这些命令在我的bash环境(msysgit)上不起作用。不指定该名称对我不利。我将在真正的Linux机器上尝试一下。
blah238 2015年

(1)并非所有版本的bash支持命令的-n选项wait。(2)我同意第一句话100%—您的解决方案开始两个过程,等待第一个过程完成,然后杀死另一个。但是问题是“……如果其中一个命令出错,另一个被终止?” 我相信您的解决方案不是OP想要的。(3)为什么更改(…) &{ …; } &&无论如何,强制列表(组命令)在子shell中运行。恕我直言,您添加了角色,并可能带来了混乱(我不得不仔细看过两次才能理解),没有任何好处。
G-Man说'Resstate Monica''Sep

1
John是正确的,它们是Web服务器,通常应该都保持运行,除非发生错误或发出终止信号。因此,我认为我们不需要检查每个进程的错误级别,而只需检查它是否仍在运行。
blah238

2
pkill对我不可用,但kill 0似乎具有相同的效果。另外,我还更新了适用于Windows的Git环境,现在看来wait -n可以使用了,因此我接受了这个答案。
blah238

1
有趣。尽管我看到某些版本的记录了该行为kill。我系统上的文档没有提及。但是,kill 0无论如何都可以。好发现!
John1024

1

这很棘手。这是我设计的;可以简化/简化它:

#!/bin/sh

pid1file=$(mktemp)
pid2file=$(mktemp)
stat1file=$(mktemp)
stat2file=$(mktemp)

while true; do sleep 42; done &
main_sleeper=$!

(cd frontend && gulp serve           & echo "$!" > "$pid1file";
    wait "$!" 2> /dev/null; echo "$?" > "$stat1file"; kill "$main_sleeper" 2> /dev/null) &
(cd backend  && gulp serve --verbose & echo "$!" > "$pid2file";
    wait "$!" 2> /dev/null; echo "$?" > "$stat2file"; kill "$main_sleeper" 2> /dev/null) &
sleep 1
wait "$main_sleeper" 2> /dev/null

if stat1=$(<"$stat1file")  &&  [ "$stat1" != "" ]  &&  [ "$stat1" != 0 ]
then
        echo "First process failed ..."
        if pid2=$(<"$pid2file")  &&  [ "$pid2" != "" ]
        then
                echo "... killing second process."
                kill "$pid2" 2> /dev/null
        fi
fi
if [ "$stat1" = "" ]  &&  \
   stat2=$(<"$stat2file")  &&  [ "$stat2" != "" ]  &&  [ "$stat2" != 0 ]
then
        echo "Second process failed ..."
        if pid1=$(<"$pid1file")  &&  [ "$pid1" != "" ]
        then
                echo "... killing first process."
                kill "$pid1" 2> /dev/null
        fi
fi

wait
if stat1=$(<"$stat1file")
then
        echo "Process 1 terminated with status $stat1."
else
        echo "Problem getting status of process 1."
fi
if stat2=$(<"$stat2file")
then
        echo "Process 2 terminated with status $stat2."
else
        echo "Problem getting status of process 2."
fi
  • 首先,启动一个while true; do sleep 42; done &永久休眠/暂停的进程()。如果您确定您的两个命令将在一定时间(例如一个小时)内终止,则可以将其更改为一次睡眠(该睡眠将超过该时间)(例如sleep 3600)。然后,您可以更改以下逻辑以将其用作超时。即,如果经过这么长时间后仍在运行,则终止它们。(请注意,上面的脚本当前不执行此操作。)
  • 启动两个异步(并发后台)进程。
    • 你不需要./cd
    • command & echo "$!" > somewhere; wait "$!" 是一个棘手的构造,它异步启动一个进程,捕获其PID,然后等待它;使它成为前台(同步)进程。但这发生(…)在整个后台的列表中,因此gulp进程确实异步运行。
    • 在任何一个gulp进程退出后,将其状态写入临时文件并终止“永久睡眠”进程。
  • sleep 1 以防止出现争用情况,在这种情况下,第一个后台进程在第二个进程有机会将其PID写入文件之前死亡。
  • 等待“永远的睡眠”过程终止。如上所述,这在任何一个gulp进程退出后都会发生。
  • 查看终止的后台进程。如果失败,请杀死另一个。
  • 如果一个进程失败而我们杀死了另一个进程,请等待第二个进程结束并将其状态保存到文件中。如果第一个过程成功完成,请等待第二个过程完成。
  • 检查两个进程的状态。

1

为了完整起见,我最终使用的是:

#!/bin/bash
(cd frontend && gulp serve) &
(cd backend && gulp serve --verbose) &
wait -n
kill 0

这对我适用于Windows 2.5.3 64位Git。较早的版本可能不接受的-n选项wait


1

在我的系统(Centos)上,wait没有,-n所以我这样做了:

{ sleep 3; echo one;  } &
FOO=$!
{ sleep 6; echo two;  } &
wait $FOO
pkill -P $$

这不是等待“两个”,而是等待第一个。但是,如果您知道首先停止哪个服务器,它仍然可以提供帮助。


是否wait具有-n选项取决于您使用的外壳,而不取决于Linux发行版。
库萨兰达
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.