2つのサーバーを起動する単純なbashスクリプトがあります。
#!/bin/bash
(cd ./frontend && gulp serve) & (cd ./backend && gulp serve --verbose)
2番目のコマンドが存在する場合、最初のコマンドは実行を続けているようです。
これをどのように変更すれば、どちらかのコマンドが終了した場合、もう一方のコマンドは終了しますか?
バックグラウンドプロセスのエラーレベルを確認する必要はありません。バックグラウンドプロセスが終了したかどうかを確認するだけです。
これは両方のプロセスを開始し、最初のプロセスが完了するのを待ってから、もう一方を強制終了します。
#!/bin/bash
{ cd ./frontend && gulp serve; } &
{ cd ./backend && gulp serve --verbose; } &
wait -n
pkill -P $$
開始:
{ cd ./frontend && gulp serve; } &
{ cd ./backend && gulp serve --verbose; } &
上記の2つのコマンドは、両方のプロセスをバックグラウンドで開始します。
待つ
wait -n
これは、いずれかのバックグラウンドジョブが終了するのを待ちます。
-n
オプションのため、これにはbash 4.3以降が必要です。
Kill
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 $$
これは「どちらか」を待つのではなく、最初のものを待ちます。ただし、どのサーバーが最初に停止するかがわかっている場合は、それでも役立ちます。
これはトリッキーです。これが私が考案したものです。それを簡素化/合理化することが可能かもしれません:
#!/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 &
)永遠にスリープ/一時停止します。 2つのコマンドが特定の時間(たとえば、1時間)以内に終了することが確実な場合は、これを、それを超える単一のスリープ(たとえば、sleep 3600
)。次に、次のロジックを変更して、これをタイムアウトとして使用できます。つまり、その時間が経過してもプロセスがまだ実行されている場合は、プロセスを強制終了します。 (上記のスクリプトは現在それを行わないことに注意してください。)./
for cd
。command & echo "$!" > somewhere; wait "$!"
は、プロセスを非同期で開始し、そのPIDを取得して、それを待機するトリッキーな構造です。フォアグラウンド(同期)プロセスのようなものにします。しかし、これは(…)
リスト全体がバックグラウンドにあるため、gulp
プロセスは非同期で実行されます。gulp
プロセスのいずれかが終了したら、そのステータスを一時ファイルに書き込み、「永久スリープ」プロセスを終了します。sleep 1
最初のバックグラウンドプロセスが停止してから2番目のプロセスがPIDをファイルに書き込む機会が得られるという競合状態から保護します。gulp
プロセスのどちらかが終了した後に発生します。完全を期すために、ここで私が最終的に使用したものを示します。
#!/bin/bash
(cd frontend && gulp serve) &
(cd backend && gulp serve --verbose) &
wait -n
kill 0
これは、Git for Windows 2.5.3 64ビットで動作します。古いバージョンはwait
の-n
オプションを受け入れない場合があります。