web-dev-qa-db-ja.com

ジョブIDを取得する方法は?

ご存知のように、シェルを使用すると、ユーザーはコマンドラインの最後で&を使用してバックグラウンドプロセスを実行できます。各バックグラウンドプロセスは、ジョブIDによって、そしてもちろん、そのPIDによって識別されます。

新しいジョブを実行すると、出力は[1] 1234のようになります(2番目の数字はプロセスIDです)。 stop 1stop %1などのコマンドを呼び出そうとすると、失敗メッセージが表示されます:stop: Unknown job: 1

stopコマンドが原因でジョブが一時停止されることを理解して、私はジョブIDを取得する方法と思って正しく実行しました。ジョブを強制終了する唯一の方法がそのプロセスIDによる場合、ジョブIDの目的は何ですか

14
Reflection

プロセスが&でバックグラウンドに送信された後、そのPIDは変数$!から取得できます。ジョブIDはjobsコマンドを使用して表示できます。-lスイッチはPIDも表示します。

 $ sleep 42 &
 [1] 5260
 $ echo $!
 5260
 $ jobs -l
 [1]  - 5260 running    sleep 42

一部のkill実装では、PIDではなくジョブIDで強制終了できます。しかし、ジョブIDのより賢明な使用法は、特定のプロセスを選択的にフォアグラウンドにすることです。 5つのプロセスをバックグラウンドで開始し、3番目のプロセスをフォアグラウンドにしたい場合は、jobsコマンドを実行して起動したプロセスを確認し、次にfg %3を実行してジョブID 3のプロセスをフォアグラウンドにできます。

24
Marco

ジョブ制御は、80年代前半にBSDシステムに追加された機能です。 cshはBSDシェルなので、最初にそのシェルで機能が導入されたのは当然のことです。ジョブ制御が非BSD Unicesおよび他のシェル(Kornシェル以降)に追加されるまで、数年待つ必要がありました。

ジョブ制御はプロセスを非同期で開始する機能ではないことに注意してください(つまり、シェルは次のプロンプトを発行する前に終了を待機していません。ボーンシェルには最初から_&_がありましたが、そうではありませんでした。 10年後まではジョブ制御をサポートします)が、同じ端末から複数のタスクを操作する機能についてです。

jobsのことであり、processesではありません。ジョブ制御を実装するために、プロセスグループが導入されました。ジョブはプロセスグループです(ジョブユーザーの観点から、プロセスグループ OS実装の観点から)。

これは通常、プロセスグループを作成および管理する対話型シェルであり、繰り返しになりますが、端末との対話がすべてです。

コマンドを実行すると、すべてのプロセスとその子孫が新しいプロセスグループに配置されます。

特定の端末でのセッションでは、1つのプロセスグループに端末への特権アクセスが許可されます。これがターミナルのフォアグラウンドプロセスグループです。そのプロセスグループは、押すとメンバーがSIGINT/SIGTSTP/SIGQUITを受け取るグループです。 CTRL-{C,Z,\}。他のプロセスグループ内のプロセスは、端末からの読み取り(および書き込み)を試みると中断されます。等.

フォアグラウンドでジョブを開始すると、インタラクティブシェルはそのプロセスグループをターミナルのフォアグラウンドプロセスグループにし、バックグラウンドで開始されたプロセスグループにはしません。

このようにして、ユーザーは端末で一度にいくつかのことを行うことができ、ジョブのリストを取得できます。ジョブのリストは、(端末への特権アクセスで)フォアグラウンド、バックグラウンド、実行中、または一時停止にできるプロセスのグループです。また、ジョブをバックグラウンド/フォアグラウンドから/に移動したり、一時停止/実行したりできます。

現在、すべてのシステムとすべてのシェルがジョブ制御をサポートしています(ただし、Bourneシェルでは、_set -m_ IIRCで明示的に要求する必要があります)。

プロンプトで開始されたすべてのコマンドには、ジョブ番号が与えられます。システムの観点からは、これらはプロセスグループIDにマップされます。

_ $ ps -fj | cat
UID        PID  PPID  PGID   SID  C STIME TTY          TIME CMD
chazelas  8237  7315  8237  8237  0 15:54 pts/7    00:00:00 /bin/zsh
chazelas 10720  8237 10720  8237  0 21:25 pts/7    00:00:00 ps -fj
chazelas 10721  8237 10720  8237  0 21:25 pts/7    00:00:00 cat
_

上記では、その_ps -j | cat_ジョブをフォアグラウンドで開始しました。 10720プロセスグループが割り当てられました(これは、psのプロセスIDでもあります)。 (tcgetpgrp()を使用して)実行しているときにターミナルのフォアグラウンドプロセスグループIDを照会した場合、10720になります。

_$ (sleep 100;:) | sleep 101 &
[1] 10787 10789
$ ps -fj
UID        PID  PPID  PGID   SID  C STIME TTY          TIME CMD
chazelas  8237  7315  8237  8237  0 15:54 pts/7    00:00:00 /bin/zsh
chazelas 10787  8237 10787  8237  0 21:33 pts/7    00:00:00 /bin/zsh
chazelas 10788 10787 10787  8237  0 21:33 pts/7    00:00:00 sleep 100
chazelas 10789  8237 10787  8237  0 21:33 pts/7    00:00:00 sleep 101
chazelas 10790  8237 10790  8237  0 21:33 pts/7    00:00:00 ps -fj
_

そのとき、私はバックグラウンドでパイプラインを開始しました。シェルはジョブ番号(1)と2つのプロセスID(_(t)csh_、pdkshおよびzshはすべてのプロセスIDを提供し、動作は他のシェルで異なります)、サブシェル用、_sleep 101_用。ちなみに、そのサブシェルは3番目のプロセス(_sleep 100_)を生成しました。シェルはそれについて何も知らないため、そのpidを報告しませんでしたが、それでも同じジョブの一部です(上記のプロセスグループ10787)。

これで、これらのジョブを操作するためのコマンドがいくつかあります:fgbgkillwaitjobs(一部のシェルには、disownと、killへのエイリアスもあり、別のデフォルトシグナルを送信します)。

これらは_%n_の形式でジョブIDを取得します。ここで、nはジョブ番号です。コマンドがfooで始まるジョブを参照する_%foo_などがあります(シェルのマニュアルを参照)。

_kill %1
_

sIGTERMシグナルをジョブ1のすべてのプロセスに送信します。_kill -s STOP %1_は、stopにエイリアスされている場合があり、代わりにSIGSTOPシグナルを送信します。

_fg %1
_

これをフォアグラウンドに置きます(端末のフォアグラウンドプロセスグループにし、SIGCONTシグナルを送信して、シェルに完了を待つように指示します)。

_bg %1
_

停止したバックグラウンドジョブを再開(SIGCONTを送信)します。

_wait %1
_

ジョブはフォアグラウンドに置かれませんが、その完了を(または中断されるまで)待機します。

_jobs
_

現在のジョブのリスト(_jobs -l_ pgidsおよび/またはpids)。

_%%_および_%+_はcurrentジョブを参照します。現在のジョブは、必ずしも最後に開始されたジョブであるとは限らないことに注意してください。これは、jobs 'の出力で_+_でマークされたジョブです。引数なしのfgまたはbgが処理するのもジョブです。中断されているジョブがある場合は、最後に中断されたジョブです。すべてが実行されている場合は、最後に開始されたジョブです。

したがって、中断されたジョブがある場合、_kill %%_はバックグラウンドで開始したばかりのジョブを強制終了しないことに注意してください。 jobsを実行して、現在実行中のジョブを確認してから、ジョブを強制終了することをお勧めします。

ジョブを処理するときはnotプロセスIDで使用する必要があることに注意してください。

_$!_は、バックグラウンドで実行された最後のコマンドのプロセスIDを返します。パイプラインの場合、それは右端のコマンドのPIDです。

_$ sleep 100 | sleep 101 & ps -fj; echo "$!"
[1] 11044 11045
UID        PID  PPID  PGID   SID  C STIME TTY          TIME CMD
chazelas  8237  7315  8237  8237  0 15:54 pts/7    00:00:00 /bin/zsh
chazelas 11044  8237 11044  8237  0 22:08 pts/7    00:00:00 sleep 100
chazelas 11045  8237 11044  8237  0 22:08 pts/7    00:00:00 sleep 101
chazelas 11046  8237 11046  8237  0 22:08 pts/7    00:00:00 ps -fj
11045
_

$以上!は11045ですが、ジョブ1のプロセスグループは11044です。_kill "$1"_の場合、_sleep 101_ではなく_sleep 100_のみを強制終了します。私が_kill -- "-$!"_(つまり、プロセスグループ11045内のすべてのプロセスを強制終了する)の場合、そのようなプロセスグループがないため、失敗します。

そこで、_kill %1_(または_kill %sleep_、または_kill %%_)を使用してそのジョブを終了する必要があります(これにより_kill -- -11044_が実行されます)。

11

stopと入力すると、実際にはupstartに付属しているコマンドを実行していたようです。これは、使い慣れたcshまたはksh組み込みstopコマンドとは関係ありません。したがって、「不明なジョブ」に関するエラーメッセージは、実行しようとしていたことには当てはまりません。

kshは、stopkill -s STOPのエイリアスとして実装します。これは、ジョブまたはプロセスグループIDの%n表記を理解します。このエイリアスは他のシェルでも機能します。たとえば、シェルがbashの場合:

$ alias stop='kill -s STOP'
$ sleep 30&
[3] 9820
$ stop %3
[3]+  Stopped     sleep 30
3
Mark Plotnick