Description: Yash のジョブ制御の動作について

# ジョブ制御

ジョブ制御とは、複数のコマンドを同時に実行し、必要に応じてそれらを中断・再開させる機能です。シェルは、オペレーティングシステムが提供する端末の機能やプロセスグループ管理機構などを用いて、ジョブ制御を実現します。

ジョブ制御が有効な時……

- シェルが起動する各​サブシェルは、シェルのプロセスグループ ID とは異なるプロセスグループ ID が割り当てられます。ただし、同じパイプライン内のサブコマンドは共通のプロセスグループ ID を持ちます。このようなプロセスグループをジョブと呼びます。

  - 例外的に、コマンド置換や​プロセスリダイレクトのために起動されたサブシェルはシェルと同じプロセスグループに属します。

- シェルがジョブを起動しそのジョブのプロセスが終了するのを待っている間にそのプロセスのどれかが停止した場合、シェルは (ジョブが終了したときと同様に) 次のコマンドの処理に移ります。このときシェルはジョブが停止したことを覚えているので、後でジョブを再開させることができます。

  - このとき、シェルが​対話モードの場合、シェルが読み込んだがまだ実行していないコマンドは破棄され、次のプロンプトが表示されます。

- ジョブが​同期的に実行される場合、そのジョブの実行中はそのジョブのプロセスグループが端末のフォアグラウンドプロセスグループになります。ジョブの実行が終了 (または停止) すると、再びシェルがフォアグラウンドになります。

- シェルが​対話モードの場合、プロンプトを出す前に毎回コマンド `jobs -n` を実行するのと同様にしてジョブの状態変化を報告します。

- 非同期コマンドの標準入力が自動的に /dev/null にリダイレクトされません。

- SIGTTIN, SIGTTOU, SIGTSTP シグナルを受けても、シェルは停止しません。コマンド置換やプロセスリダイレクトのために起動されたサブシェルは SIGTSTP シグナルを受けても停止しません。

- 特殊パラメータ `-` の値に `m` が含まれます。

- Wait 組込みコマンドで待っているジョブが終了したとき、そのことを示すメッセージを出力します。(対話モードの時のみ。​POSIX 準拠モードを除く)

ジョブ制御が無効な時、シェルが起動する各プロセスはシェルと同じプロセスグループに属しますが、実行した​非同期コマンドはそれぞれジョブ制御の対象となっていないジョブとして扱います。

ここでジョブ制御に関連する組込みコマンドを簡単に紹介します。

jobs

  現在シェルが管理しているジョブを表示します。

fg および bg

  ジョブをフォアグラウンドまたはバックグラウンドで実行します。主に停止したジョブを再開させるのに使います。

wait

  ジョブが終了するまで待ちます。

disown

  ジョブの存在を忘れます。

kill

  プロセスにシグナルを送ります。

対話モードでジョブ制御が有効な時、シェルはプロンプトを出す直前にジョブの状態変化を報告します。これ以外のタイミングで状態変化を報告してほしい場合は、以下の​オプションを指定することができます。

notify

  タイミングにかかわらず、ジョブの状態が変化したら直ちにそれを報告します。

notify-le

  行編集を行っている最中にジョブの状態が変化したら直ちにそれを報告します。

シェルが管理しているジョブは以下のタイミングで削除されます。

- ジョブの実行が終了した後、そのことを jobs 組込みコマンドで表示したとき

- Wait 組込みコマンドでジョブの終了を待ったとき

- Disown 組込みコマンドでジョブを削除したとき

対話シェルが自動的にジョブの状態変化を報告するときにはジョブは削除されません。

## ジョブ ID

いくつかの​組込みコマンドでは、操作対象のジョブを指定するためにジョブ ID という以下のような記法を用います。

`%`
`%%`
`%+`

  現在のジョブ

`%-`

  前のジョブ

`%n`

  ジョブ番号が n のジョブ (n は自然数)

`%文字列`

  ジョブ名が文字列で始まるジョブ

`%?文字列`

  ジョブ名が文字列を含むジョブ

現在のジョブ及び前のジョブとは、シェルが特定の方法で選んだジョブのことで、​fg 組込みコマンドなどでジョブを選択しやすくするために用意されています。現在のジョブと前のジョブは以下の規則を満たすように選ばれます。

- 停止中のジョブがある場合は、現在のジョブはその中から選ばれます。

- 現在のジョブ以外に停止中のジョブがある場合は、前のジョブはその中から選ばれます。

- 現在のジョブと前のジョブは異なるジョブになるように選ばれます。ジョブが一つしかないときはそれが現在のジョブになり、前のジョブはなくなります。

- 現在のジョブが終了したときは、前のジョブが現在のジョブになります。これ以外に現在のジョブが変更される場合は、元の現在のジョブは前のジョブになります。

- フォアグラウンドで実行していたジョブが停止したときは、そのジョブは現在のジョブになります。

Yash には、現在のジョブを選択する方針を指示するためにいくつかの​オプションが用意されています。ただしこれらのオプションよりも上記の規則のほうが優先します。

cur-async

  新しく​非同期コマンドを起動したとき、それは現在のジョブになります。

cur-bg

  Bg 組込みコマンドでジョブを再開したとき、そのジョブは現在のジョブになります。

cur-stop

  実行中のジョブが停止したとき、そのジョブは現在のジョブになります。

これらの規則・オプションに反しない限り、一度選ばれた現在のジョブ・前のジョブはずっと現在のジョブ・前のジョブのままです。

POSIX は現在のジョブ・前のジョブの選択方法を細かく定めていないため、他のシェルでは選び方が異なることがあります。