ジョブ制御
ジョブ制御とは、複数のコマンドを同時に実行し、必要に応じてそれらを中断・再開させる機能です。シェルは、オペレーティングシステムが提供する端末の機能やプロセスグループ管理機構などを用いて、ジョブ制御を実現します。
ジョブ制御が有効な時……
- シェルが起動する各プロセスは、パイプラインごとに共通の一意なプロセスグループに属します。すなわち、シェルが起動するコマンドはそれぞれパイプラインごとにジョブとして扱われます。
- シェルがジョブを起動しそのジョブのプロセスが終了するのを待っている間にそのプロセスが停止した場合、シェルは (プロセスが実際に終了したときと同様に) 次のコマンドの処理に移ります。このときシェルはジョブが停止したことを覚えているので、後でジョブを再開させることができます。
- ジョブが同期的に実行される場合、そのジョブの実行中はそのジョブのプロセスグループが端末のフォアグラウンドプロセスグループになります。ジョブの実行が終了 (または停止) すると、再びシェルがフォアグラウンドになります。
- コマンド置換のコマンドを実行するサブシェルもまた独立したプロセスグループに属します。しかしシェルはこれをジョブとしては扱わないため、停止・再開させることはできません。
- シェルが対話モードの場合、プロンプトを出す前に毎回コマンド
jobs -n
を実行するのと同様にしてジョブの状態変化を報告します。
- 非同期コマンドの標準入力が自動的に /dev/null にリダイレクトされません。(POSIX 準拠モードのときを除く)
- SIGTSTP シグナルを受けても、シェルは停止しません。
- 特殊パラメータ
-
の値に m
が含まれます。
- Wait 組込みコマンドで待っているジョブが終了したとき、そのことを示すメッセージを出力します。(対話モードの時のみ。POSIX 準拠モードを除く)
ジョブ制御が無効な時、シェルが起動する各プロセスはシェルと同じプロセスグループに属しますが、実行した非同期コマンドはそれぞれジョブ制御の対象となっていないジョブとして扱います。
ここでジョブ制御に関連する組込みコマンドを簡単に紹介します。
- Jobs
- 現在シェルが管理しているジョブを表示します。
- Fg, bg
- ジョブをフォアグラウンドまたはバックグラウンドで実行します。主に停止したジョブを再開させるのに使います。
- Wait
- ジョブが終了 (または停止) するまで待ちます。
- Disown
- ジョブの存在を忘れます。
- Kill
- プロセスにシグナルを送ります。
対話モードでジョブ制御が有効な時、シェルはプロンプトを出す直前にジョブの状態変化を報告します。これ以外のタイミングで状態変化を報告してほしい場合は、以下のオプションを指定することができます。
- Notify
- タイミングにかかわらず、ジョブの状態が変化したら直ちにそれを報告します。
- Notifyle
- 行編集を行っている最中にジョブの状態が変化したら直ちにそれを報告します。
シェルが管理しているジョブは以下のタイミングで削除されます。
ジョブ ID
いくつかの組込みコマンドでは、操作対象のジョブを指定するためにジョブ ID という以下のような記法を用います。
%
, %%
, %+
- 現在のジョブ。
%-
- 前のジョブ。
%n
(ただし n は自然数)
- ジョブ番号が n のジョブ。
%文字列
- ジョブ名が文字列で始まるジョブ。
%?文字列
- ジョブ名が文字列を含むジョブ。
現在のジョブ及び前のジョブとは、シェルが特定の方法で選んだジョブのことで、fg 組込みコマンドなどでジョブを選択しやすくするために用意されています。現在のジョブと前のジョブは以下の規則を満たすように選ばれます。
- 停止中のジョブがある場合は、現在のジョブはその中から選ばれます。
- 現在のジョブ以外に停止中のジョブがある場合は、前のジョブはその中から選ばれます。
- 現在のジョブと前のジョブは異なるジョブになるように選ばれます。ジョブが一つしかないときはそれが現在のジョブになり、前のジョブはなくなります。
- 現在のジョブが終了したときは、前のジョブが現在のジョブになります。これ以外に現在のジョブが変更される場合は、元の現在のジョブは前のジョブになります。
- フォアグラウンドで実行していたジョブが停止したときは、そのジョブは現在のジョブになります。
Yash には、現在のジョブを選択する方針を指示するためにいくつかのオプションが用意されています。ただしこれらのオプションよりも上記の規則のほうが優先します。
- Curasync
- 新しく非同期コマンドを起動したとき、それは現在のジョブになります。
- Curbg
- Bg 組込みコマンドでジョブを再開したとき、そのジョブは現在のジョブになります。
- Curstop
- 実行中のジョブが停止したとき、そのジョブは現在のジョブになります。
これらの規則・オプションに反しない限り、一度選ばれた現在のジョブ・前のジョブはずっと現在のジョブ・前のジョブのままです。
POSIX は現在のジョブ・前のジョブの選択方法を細かく定めていないため、他のシェルでは選び方が異なることがあります。