web-dev-qa-db-ja.com

複数のコマンドを実行し、bashでそれらを1つとして強制終了します

1つのシェルで複数のコマンド(プロセス)を実行したい。それらのすべてが独自の連続出力を持ち、停止しません。バックグラウンドブレークで実行すると Ctrl-C。それらをすべて停止できるように、それらを単一のプロセス(サブシェル、たぶん?)として実行したい Ctrl-C

具体的には、mocha(監視モード)を使用して単体テストを実行し、サーバーを実行してファイルの前処理(監視モード)を実行し、それぞれの出力を1つのターミナルウィンドウで確認します。基本的に、タスクランナーの使用は避けたいです。

バックグラウンドでプロセスを実行することでそれを実現できます(&)、しかし、私はそれらを前景に入れてそれらを止めなければなりません。それらをラップするプロセスが欲しいのですが、プロセスを停止すると、その「子」が停止します。

55
user1876909

コマンドを同時に実行するには、&コマンド区切り文字を使用できます。

~$ command1 & command2 & command3

これによりcommand1が起動し、バックグラウンドで実行されます。 command2も同様です。次に、command3を正常に開始します。

すべてのコマンドの出力は一緒に文字化けしますが、それがあなたにとって問題でなければ、それが解決策です。

後で出力を個別に確認する場合は、各コマンドの出力をteeにパイプして、出力をミラーリングするファイルを指定できます。

~$ command1 | tee 1.log & command2 | tee 2.log & command3 | tee 3.log

出力はおそらく非常に乱雑になります。これに対抗するには、sedを使用して、すべてのコマンドの出力にプレフィックスを付けることができます。

~$ echo 'Output of command 1' | sed -e 's/^/[Command1] /' 
[Command1] Output of command 1

したがって、これらすべてを組み合わせると、次のようになります。

~$ command1 | tee 1.log | sed -e 's/^/[Command1] /' & command2 | tee 2.log | sed -e 's/^/[Command2] /' & command3 | tee 3.log | sed -e 's/^/[Command3] /'
[Command1] Starting command1
[Command2] Starting command2
[Command1] Finished
[Command3] Starting command3

これは、おそらくあなたが目にするであろうものの非常に理想化されたバージョンです。しかし、私が今考えることができる最高のものです。

それらすべてを一度に停止したい場合は、trapでビルドを使用できます。

~$ trap 'kill %1; kill %2' SIGINT
~$ command1 & command2 & command3

これにより、バックグラウンドでcommand1およびcommand2が実行され、フォアグラウンドでcommand3が実行されます。これにより、 Ctrl+C

最後のプロセスを強制終了すると Ctrl+C kill %1; kill %2コマンドが実行されます。これは、その実行をINTerupt SIGnalの受信に接続したためです。 Ctrl+C

これらはそれぞれ、1番目と2番目のバックグラウンドプロセス(command1command2)を強制終了します。 trap - SIGINTを使用してコマンドを終了したら、トラップを削除することを忘れないでください。

コマンドの完全なモンスター:

~$ trap 'kill %1; kill %2' SIGINT
~$ command1 | tee 1.log | sed -e 's/^/[Command1] /' & command2 | tee 2.log | sed -e 's/^/[Command2] /' & command3 | tee 3.log | sed -e 's/^/[Command3] /'

もちろん、 screen を確認することもできます。これにより、コンソールを必要な数の個別のコンソールに分割できます。したがって、すべてのコマンドを個別に監視できますが、同時に監視できます。

70
Minix

同じ プロセスグループ でそれらを実行するようにアレンジすると、一連のプロセスを簡単に強制終了できます。

Linuxには、新しいプロセスグループでプログラムを実行するためのユーティリティ setsid が用意されています(新しいセッションでも同様ですが、それは問題ではありません)。 (これは 実行可能ですが、より複雑です なしでsetsidです。)

プロセスグループのプロセスグループID(PGID)は、グループ内の元の親プロセスのプロセスIDです。プロセスグループ内のすべてのプロセスを強制終了するには、PGIDの負の値をkillシステムコールまたはコマンドに渡します。 PGIDは、このPIDを持つ元のプロセスが停止した場合でも有効です(少し混乱する可能性があります)。

setsid sh -c 'command1 & command2 & command3' &
pgid=$!
echo "Background tasks are running in process group $pgid, kill with kill -TERM -$pgid"

non-interactiveシェルからバックグラウンドでプロセスを実行する場合、それらはすべてシェルのプロセスグループに残ります。バックグラウンドプロセスが独自のプロセスグループで実行されるのは、対話型シェルのみです。したがって、フォアグラウンドに残っている非インタラクティブシェルからコマンドをフォークすると、 Ctrl+C それらすべてを殺します。ビルトインwaitを使用して、すべてのコマンドが終了するまでシェルを待機させます。

sh -c 'command1 & command2 & command3 & wait'
# Press Ctrl+C to kill them all

これがまだここにないことに驚いていますが、私のお気に入りの方法は、バックグラウンドコマンドで subshel​​ls を使用することです。使用法:

(command1 & command2 & command3)

出力は両方から見ることができ、すべてのbashコマンドと便利な機能は引き続き利用できます。 Ctrl-c それらすべてを殺します。私は通常、これを使用してライブデバッグクライアントファイルサーバーとバックエンドサーバーを同時に起動するので、必要なときに両方を簡単に強制終了できます。

これが機能する方法はcommand1およびcommand2は新しいサブシェルインスタンスのバックグラウンドで実行されており、command3はそのインスタンスのフォアグラウンドにあり、サブシェルは最初に Ctrl-c キーを押します。それは、誰がどのプロセスを所有し、いつバックグラウンドプログラムが強制終了されるかに関して、bashスクリプトを実行するのと非常によく似ています。

17
jv-dev

セミコロン;または&&は次のように使用できます。

cmd1; cmd2   # Runs both the commands, even if the first one exits with a non-zero status.

cmd1 && cmd2 # Only runs the second command if the first one was successful.
0
serenesat