我有一个简单的 bash 脚本来启动两个服务器:
#!/bin/bash
(cd ./frontend && gulp serve) & (cd ./backend && gulp serve --verbose)
如果第二个命令退出,则第一个命令似乎继续运行。
如何更改此设置,以便如果任一命令退出,另一个命令就会终止?
请注意,我们不需要检查后台进程的错误级别,只需检查它们是否已退出。
答案1
这会启动两个进程,等待第一个进程完成,然后杀死另一个进程:
#!/bin/bash
{ cd ./frontend && gulp serve; } &
{ cd ./backend && gulp serve --verbose; } &
wait -n
pkill -P $$
怎么运行的
开始:
{ cd ./frontend && gulp serve; } & { cd ./backend && gulp serve --verbose; } &
上述两个命令在后台启动两个进程。
等待
wait -n
这会等待任一后台作业终止。
由于该
-n
选项的原因,这需要 bash 4.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
第二个进程将首先终止。无论哪种情况,人们都可以看到这按预期运行。
答案2
在我的系统(Centos)上,wait
没有-n
,所以我这样做了:
{ sleep 3; echo one; } &
FOO=$!
{ sleep 6; echo two; } &
wait $FOO
pkill -P $$
这不会等待“任一”,而是等待第一个。但如果您知道哪个服务器将首先停止,它仍然会有所帮助。
答案3
这很棘手。这是我的设计;也许可以简化/精简它:
#!/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
如上所述,进程退出。 - 查看哪个后台进程终止了。如果失败了,就杀死另一个。
- 如果一个进程失败并且我们杀死了另一个进程,请等待第二个进程结束并将其状态保存到文件中。如果第一个进程成功完成,请等待第二个进程完成。
- 检查两个进程的状态。
答案4
为了完整起见,这是我最终使用的:
#!/bin/bash
(cd frontend && gulp serve) &
(cd backend && gulp serve --verbose) &
wait -n
kill 0
这对我来说适用于 Windows 2.5.3 64 位的 Git。旧版本可能不接受-n
上的选项wait
。