バックグラウンドプロセスのプロセスIDを取得する方法


375

シェルスクリプトからバックグラウンドプロセスを開始し、スクリプトが終了したらこのプロセスを強制終了したいと思います。

シェルスクリプトからこのプロセスのPIDを取得するにはどうすればよいですか?私が見る限り、変数$!にはバックグラウンドプロセスではなく、現在のスクリプトのPIDが含まれています。


8
$!正しい。BGでスクリプトを開始しますか?サンプルしてください。
pixelbeat 2009

7
はい、$!正しい。私は間違っていた。
Volodymyr Bezuglyy 2009

11
$$には、現在のスクリプトPIDが含まれています。
HUB

回答:


583

開始時にバックグラウンドプロセスのPIDを保存する必要があります。

foo &
FOO_PID=$!
# do other stuff
kill $FOO_PID

ジョブ制御はインタラクティブな機能であり、制御端末に関連付けられているため、使用できません。スクリプトには必ずしもターミナルが接続されている必要はないため、ジョブ制御を利用できるとは限りません。


22
$から!最後のバックグラウンドプロセスのPIDを返します。それが可能であることとの間に何かが始まるfoo$!、私たちは代わりにその何かのpidを取得するfooの?
WiSaGaN 2013年

53
@WiSaGaN:いいえ。これらの線の間には何もありません。システム上の他のアクティビティはこれに影響しません。$!そのシェルの最後のバックグラウンドプロセスのPIDに展開されます
camh 2013年

8
... fooたまたま複数のパイプされたコマンド(たとえばtail -f somefile.txt | grep sometext)である場合に役立ちます そのような場合、grepコマンドのPIDは、$!それが探していたものであれば、tailコマンドではなくから取得します。この場合はjobsまたはpsなどを使用する必要があります。
John Rix 2014年

1
@JohnRix:必ずしもそうではありません。あなたはpid ofを取得しgrepますが、それをkillすると、tailはパイプに書き込もうとしたときにSIGPIPEを取得します。しかし、トリッキーなプロセス管理/制御に取り掛かろうとすると、bash / shellは非常に苦痛になります。
camh

5
(回答へのコメント)開始したプロセスのpidを取得する方法:おお、そして「oneliner」:/bin/sh -c 'echo $$>/tmp/my.pid && exec program args' &sysfault Nov 24 '10 at 14:28
imz-Ivan Zakharyaschev

148

jobs -lコマンドを使用して、特定のジョブにアクセスできますL

^Z
[1]+  Stopped                 guard

my_mac:workspace r$ jobs -l
[1]+ 46841 Suspended: 18           guard

この場合、46841がPIDです。

からhelp jobs

-lジョブのプロセスグループIDと作業ディレクトリを報告します。

jobs -p PIDのみを表示する別のオプションです。


これをシェルスクリプトで使用するには、出力を処理する必要があります。
Phil

6
@Phil PIDのみを一覧表示するには:ジョブ-p。特定のジョブのpidをリストするには、jobs -p%3。出力を処理する必要はありません。
Erik Aronesty 2014

1
@Erikはさまざまなコマンド/引数を使用して、私のコメントのコンテキストを変更します。追加の引数が示唆されない場合、出力には処理が必要です。答えの改善を提案してください!
Phil

$!開始直後のPIDの保存は、ほとんどの場合、移植性が高く、簡単です。それが現在受け入れられている答えです。
tripleee 2017

jobs -pjobs -lLubuntu 16.4 と同じ結果を返しました
Timo

46
  • $$ 現在のスクリプトのpidです
  • $! 最後のバックグラウンドプロセスのPIDです

以下は、bashセッションからのトランスクリプトのサンプルです(%1から見たバックグラウンドプロセスの序数を指しますjobs)。

$ echo $$
3748

$ sleep 100 &
[1] 192

$ echo $!
192

$ kill %1

[1]+  Terminated              sleep 100

エコーは、%1一方で私のUbuntu上でバックグラウンドプロセスを返さないecho $!
ティモ

25

bashスクリプトのすべての子プロセスを強制終了するさらに簡単な方法:

pkill -P $$

-Pフラグはと同じように動作pkillし、pgrepだけで、それは子プロセスを取得します- pkill子プロセスが殺されるととpgrep子のPID stdoutに出力されています。


とても便利!これは、開いているプロセスをバックグラウンドに残さないようにするための最良の方法です。
lepe

@lepe:違います。祖父母の場合、これは機能しません。bash -c 'bash -c "sleep 300 &"' & 実行後pgrep -P $$は何も表示されません。これは、スリープがシェルの直接の子ではないためです。
Petre

1
@AlexeyPolonsky:する必要があります:スクリプトではなく、シェルのすべての子プロシージャを強制終了します。$$現在のシェルを参照するためです。
Timo

パフォーマンスbash -c 'bash -c "sleep 300 &"' & ; pgrep -P $$、私は標準出力に乗る[1] <pid>. So at least it shows something, but this is probably not the output of pgrep`に参加します
Timo

プロセスでさえ、それらを親プロセスから切り離すことができます。単純なトリックは、fork(孫を作成)を呼び出し、孫が作業を続けている間に子プロセスを終了させることです。(デーモン化がキーワード)しかし、子供がpkill -Pを実行し続けても、孫に信号を伝達するには十分ではありません。依存するプロセスツリー全体を追跡するには、pstreeのようなツールが必要です。ただし、親はプロセス1であるため、プロセスから開始されたデーモンはキャッチされません。例:bash -c 'bash -c "sleep 10 & wait $!"' & sleep 0.1; pstree -p $$
Pauli Nieminen

4

これは私がやったことです。それをチェックして、それが役に立てば幸いです。

#!/bin/bash
#
# So something to show.
echo "UNO" >  UNO.txt
echo "DOS" >  DOS.txt
#
# Initialize Pid List
dPidLst=""
#
# Generate background processes
tail -f UNO.txt&
dPidLst="$dPidLst $!"
tail -f DOS.txt&
dPidLst="$dPidLst $!"
#
# Report process IDs
echo PID=$$
echo dPidLst=$dPidLst
#
# Show process on current shell
ps -f
#
# Start killing background processes from list
for dPid in $dPidLst
do
        echo killing $dPid. Process is still there.
        ps | grep $dPid
        kill $dPid
        ps | grep $dPid
        echo Just ran "'"ps"'" command, $dPid must not show again.
done

:そして、同じようにそれを実行./bgkill.shもちろんの適切な権限を持ちます

root@umsstd22 [P]:~# ./bgkill.sh
PID=23757
dPidLst= 23758 23759
UNO
DOS
UID        PID  PPID  C STIME TTY          TIME CMD
root      3937  3935  0 11:07 pts/5    00:00:00 -bash
root     23757  3937  0 11:55 pts/5    00:00:00 /bin/bash ./bgkill.sh
root     23758 23757  0 11:55 pts/5    00:00:00 tail -f UNO.txt
root     23759 23757  0 11:55 pts/5    00:00:00 tail -f DOS.txt
root     23760 23757  0 11:55 pts/5    00:00:00 ps -f
killing 23758. Process is still there.
23758 pts/5    00:00:00 tail
./bgkill.sh: line 24: 23758 Terminated              tail -f UNO.txt
Just ran 'ps' command, 23758 must not show again.
killing 23759. Process is still there.
23759 pts/5    00:00:00 tail
./bgkill.sh: line 24: 23759 Terminated              tail -f DOS.txt
Just ran 'ps' command, 23759 must not show again.
root@umsstd22 [P]:~# ps -f
UID        PID  PPID  C STIME TTY          TIME CMD
root      3937  3935  0 11:07 pts/5    00:00:00 -bash
root     24200  3937  0 11:56 pts/5    00:00:00 ps -f

3

また、pstreeを使用できる場合もあります。

pstree -p user

これは通常、「ユーザー」のすべてのプロセスのテキスト表現を提供し、-pオプションはプロセスIDを提供します。私が理解している限り、現在のシェルがプロセスを所有していることに依存していません。フォークも表示されます。


1
これをシェルスクリプトで使用するには、出力を頻繁に処理する必要があります。
Phil

1

pgrep親プロセスのすべての子PIDを取得できます。前述のとおり$$、現在のスクリプトのPIDです。したがって、後からクリーンアップするスクリプトが必要な場合は、これでうまくいきます。

trap 'kill $( pgrep -P $$ | tr "\n" " " )' SIGINT SIGTERM EXIT

これはたくさん殺しませんか?
Phil

はい、それは、質問が維持言及したことはない、いくつかの終了時に生きている背景の子供たちを。
errant.info 2013年

trap 'pkill -P $$' SIGING SIGTERM EXITシンプルに見えますが、テストしていません。
Petre

互換性のために、SIGプレフィックスを使用しないでください。これはPOSIXで許可されていますが、実装サポートする可能性のある拡張機能として次のとおりです。
josch 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.