2つのバックグラウンドコマンドが与えられた場合、そのうちの1つが終了すると、残りのコマンドを終了します。

2つのバックグラウンドコマンドが与えられた場合、そのうちの1つが終了すると、残りのコマンドを終了します。

2つのサーバーを起動する単純なbashスクリプトがあります。

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

2 番目のコマンドが終了すると、最初のコマンドが実行され続けるように見えます。

どちらのコマンドが終了したら、他のコマンドも終了するようにこれをどのように変更できますか?

バックグラウンドプロセスのエラーレベルを確認する必要はなく、終了したかどうかを確認するだけです。

答え1

これにより、2つのプロセスが開始され、最初のプロセスが完了するのを待ってから、別のプロセスが終了します。

#!/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つのコマンドは、バックグラウンドで2つのプロセスを開始します。

  2. 待つ

    wait -n
    

    バックグラウンドジョブが終了するのを待ちます。

    このオプションにより、-nbash 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 12番目のプロセスが最初に終了します。どちらの場合も、期待どおりに機能することがわかります。

答え2

私のシステム(Centos)ではwaitnoなので、-n次のようにしました。

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

これは「2つのうちの1つ」を待たずに最初のものを待ちます。ただし、どのサーバーが最初に停止するのかを知ると、まだ役に立ちます。

答え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 &永久に休止/一時停止するプロセスを開始します()。特定の時間(たとえば、1時間)以内に2つのコマンドが終了することが確実である場合は、その時間(たとえばsleep 3600)の後にシングルスリープモードに変更できます。その後、次のロジックを変更してタイムアウトとして使用できます。つまり、この時間が経過しても実行中のプロセスを終了できます。 (上記のスクリプトは現在これを行いません。)
  • 2つの非同期(同時バックグラウンド)プロセスを開始します。
    • あなたは必要ありません./cd
    • command & echo "$!" > somewhere; wait "$!" プロセスを非同期で開始し、PIDをキャプチャした後、これをフォアグラウンド(同期)プロセスにする厳しい設定です。ただし、これは(…)バックグラウンドのリスト全体で発生するため、gulpプロセスは非同期的に実行されます。
    • 両方のプロセスのいずれかがgulp終了すると、その状態が一時ファイルに書き込まれ、「永久にスリープ」プロセスが終了します。
  • sleep 12番目のバックグラウンドプロセスは、そのPIDをファイルに書き込む前に最初のバックグラウンドプロセスが終了する競合状態を防ぎます。
  • 「永久にスリープ」プロセスが終了するのを待ちます。この仕事は後に起こる誰でもgulp上記のようにプロセスは終了します。
  • どのバックグラウンドプロセスが終了したかを確認してください。失敗したら他人を殺す
  • あるプロセスが失敗し、別のプロセスを終了すると、2番目のプロセスが終了するのを待ってからその状態をファイルに保存します。最初のプロセスが正常に完了したら、2番目のプロセスが完了するのを待ちます。
  • 両方のプロセスの状態を確認してください。

答え4

完全性のためにこれが私が使用したものです。

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

これは、Windows 2.5.3 64ビット用のGitで動作します。以前のバージョンでは上記の-nオプションは許可されていない可能性がありますwait

関連情報