1つのシェルで複数のコマンド(プロセス)を実行したい。それらのすべてが独自の連続出力を持ち、停止しません。バックグラウンドブレークで実行すると Ctrl-C。それらをすべて停止できるように、それらを単一のプロセス(サブシェル、たぶん?)として実行したい Ctrl-C。
具体的には、mocha
(監視モード)を使用して単体テストを実行し、サーバーを実行してファイルの前処理(監視モード)を実行し、それぞれの出力を1つのターミナルウィンドウで確認します。基本的に、タスクランナーの使用は避けたいです。
バックグラウンドでプロセスを実行することでそれを実現できます(&
)、しかし、私はそれらを前景に入れてそれらを止めなければなりません。それらをラップするプロセスが欲しいのですが、プロセスを停止すると、その「子」が停止します。
コマンドを同時に実行するには、&
コマンド区切り文字を使用できます。
~$ 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番目のバックグラウンドプロセス(command1
とcommand2
)を強制終了します。 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 を確認することもできます。これにより、コンソールを必要な数の個別のコンソールに分割できます。したがって、すべてのコマンドを個別に監視できますが、同時に監視できます。
同じ プロセスグループ でそれらを実行するようにアレンジすると、一連のプロセスを簡単に強制終了できます。
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
これがまだここにないことに驚いていますが、私のお気に入りの方法は、バックグラウンドコマンドで subshells を使用することです。使用法:
(command1 & command2 & command3)
出力は両方から見ることができ、すべてのbashコマンドと便利な機能は引き続き利用できます。 Ctrl-c それらすべてを殺します。私は通常、これを使用してライブデバッグクライアントファイルサーバーとバックエンドサーバーを同時に起動するので、必要なときに両方を簡単に強制終了できます。
これが機能する方法はcommand1
およびcommand2
は新しいサブシェルインスタンスのバックグラウンドで実行されており、command3
はそのインスタンスのフォアグラウンドにあり、サブシェルは最初に Ctrl-c キーを押します。それは、誰がどのプロセスを所有し、いつバックグラウンドプログラムが強制終了されるかに関して、bashスクリプトを実行するのと非常によく似ています。
セミコロン;
または&&
は次のように使用できます。
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.