2つのバックグラウンドコマンドが与えられた場合、どちらかが終了したときに残りのコマンドを終了します


14

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

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

2番目のコマンドが終了すると、最初のコマンドが実行を継続しているようです。

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

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


どうしてgulp ./fronend/serve && gulp ./backend/serve --verbose
heemayl

serveはファイルではなく引数なので、現在のディレクトリを設定する必要があります。
blah238

1
また、これらは同時に実行する必要がある長時間実行されるプロセスです。それが明確でない場合は申し訳ありません。
blah238

回答:


21

これにより、両方のプロセスが開始され、最初のプロセスが終了してから他のプロセスが終了するまで待機します。

#!/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. 待つ

    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

上記のスクリプトはasとして実行できbash script.sh 1 3、最初のプロセスが最初に終了します。または、bash script.sh 3 1次のように実行すると、2番目のプロセスが最初に終了します。どちらの場合でも、これが希望どおりに動作することがわかります。


これは素晴らしいですね。残念ながら、これらのコマンドは私のbash環境(msysgit)では機能しません。それを指定しないのは悪いことです。ただし、実際のLinuxボックスで試してみます。
blah238

(1)すべてのバージョンがコマンドのオプションをbashサポートしているわけではありません。(2)最初の文に100%同意します—ソリューションは2つのプロセスを開始し、最初のプロセスが終了するのを待ってから、もう一方を終了します。しかし、質問は「…いずれかのコマンドがエラーになった場合、もう一方は終了しますか?」あなたのソリューションはOPが望んでいるものではないと思います。(3)に変更した理由は?力は、リスト(groupコマンドは)とにかくサブシェルで実行します。私見、あなたはキャラクターを追加しました、そしておそらく混乱をもたらしました(私はそれを理解するために二度見なければなりませんでした)利益なし。-nwait(…) &{ …; } &&
G-Manが「

1
ジョンは正しいです。エラーが発生するか、終了するように信号が送られない限り、通常は両方とも実行し続けるWebサーバーです。したがって、各プロセスのエラーレベルをチェックする必要はないと思います。ただ、まだ実行されているかどうかだけです。
blah238

2
pkill私には利用できませんがkill 0、同じ効果があるようです。また、Git for Windows環境を更新しましたが、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 &永久にスリープ/一時停止するプロセス()を開始します。2つのコマンドが特定の時間(たとえば、1時間)以内に終了することが確実な場合、これを超える単一のスリープ(たとえば、sleep 3600)に変更できます。その後、次のロジックを変更して、これをタイムアウトとして使用できます。つまり、その時間が経過してもプロセスが実行されている場合は、プロセスを強制終了します。(上記のスクリプトは現在それを行っていないことに注意してください。)
  • 2つの非同期(同時バックグラウンド)プロセスを開始します。
    • あなたは必要としない./ためにcd
    • command & echo "$!" > somewhere; wait "$!" プロセスを非同期で開始し、PIDをキャプチャしてから待機するトリッキーな構造です。フォアグラウンド(同期)プロセスの並べ替えにします。しかし(…)、これは完全にバックグラウンドにあるリスト内で発生するため、gulpプロセスは非同期で実行されます。
    • いずれかのgulpプロセスが終了したら、そのステータスを一時ファイルに書き込み、「永久スリープ」プロセスを強制終了します。
  • sleep 1 2番目のプロセスがPIDをファイルに書き込む機会を得る前に、最初のバックグラウンドプロセスが停止する競合状態から保護します。
  • 「永久スリープ」プロセスが終了するまで待ちます。これは、上記のように、いずれかgulpプロセスが終了した後に発生します
  • 終了したバックグラウンドプロセスを確認します。失敗した場合は、もう一方を殺します。
  • 1つのプロセスが失敗し、他のプロセスを強制終了した場合、2番目のプロセスが終了してそのステータスをファイルに保存するのを待ちます。最初のプロセスが正常に終了した場合、2番目のプロセスが終了するまで待ちます。
  • 2つのプロセスのステータスを確認します。

1

完全を期すために、私が最終的に使用したものは次のとおりです。

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

これはGit for Windows 2.5.3 64ビットで動作します。古いバージョンでは、の-nオプションを受け入れない場合がありますwait


1

私のシステム(Centos)にwaitはない-nので、これを行いました:

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

これは「どちらか」を待たず、むしろ最初の1つを待ちます。ただし、どのサーバーが最初に停止するかを知っていれば、それでも役立ちます。


かどうか wait-nオプションがあるは、Linuxディストリビューションではなく、使用しているシェルに依存します。
クサラナナンダ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.