この質問では:シェルスクリプトで最後に実行されたコマンドのpidを取得する方法は?
スレッドは素晴らしく明確ですが、コマンドがバックグラウンドで実行されるために実際に最後に「&」が必要でない場合はどうなりますか?では、PIDを取得できますか?
ps
注:私はandを使用したくありませんgrep
。理論的には、プロセスのPIDはコマンドの実行後に見つける必要があるため、この質問をします。
バックグラウンドアプリの例 - これは私が必要としません。独自にバックグラウンドに入るすべてのものに対する一般的な解決策が必要です(誰かが「前景」オプションがまったくないことを強制します)。ただし、ソリューションをテストするには、次の手順を実行します。すばらしく働きます:
私のアプリケーションスクリプト:
#!/bin/bash
(
sleep 10
echo test
sleep 5
) &
ご覧のとおり、「&」はスクリプトの外部ではなく内部にあるため、スクリプトの起動に & は必要ありません。
./my-app ; echo $! | od -c ; jobs | od -c
0000000 \n
0000001
0000000
$! null でjobs
何も返しません。 10秒後も「TEST」メッセージが表示され続けます。
ps x | grep my-app | grep -v grep
14986 pts/5 S 0:00 /bin/bash ./my-app
更新、bash操作がここで機能しないといういくつかの意見が出た後、解決策を探したいと思い、私の希望はbashに限定されません。
答え1
到着背景独自に、アプリケーションは子プロセスを分岐し、親プロセスを終了します。シェルはコマンド自体を実行する前に分岐されたプロセスなので、親プロセスについて知っていますが、そのプロセスを作成できる子や孫を見る方法はありません。
これらのバックグラウンドコマンドのより簡単な例は次のとおりです。
$ sh -c 'ps -j; sleep 10 &'
PID PGID SID TTY TIME CMD
6562 6562 14469 pts/13 00:00:00 sh
6563 6562 14469 pts/13 00:00:00 ps
14469 14469 14469 pts/13 00:00:00 zsh
私はプロセスとそのプロセスグループをzsh
理解しています。sh
しかし、見えるのは6562のプロセス終了だけです。 6562プロセスが6563プロセスを作成したかどうかを知る方法はありません。
$ ps -j
PID PGID SID TTY TIME CMD
6564 6562 14469 pts/13 00:00:00 sleep
6565 6565 14469 pts/13 00:00:00 ps
14469 14469 14469 pts/13 00:00:00 zsh
ただし、実行中のプロセスも対応する6562プロセスグループにあることがわかりますsleep
(ただし、コマンドが新しいプロセスグループまたはセッションを開始するのをやめる方法はありません(通常はデーモンが実行するように)。
これらのプロセスグループは、シェルの相互作用中にのみ作成されます。
あなたができるもう一つのことは次のとおりです。
cmd | cat &
wait
cmdによって生成されたプロセスが標準出力を閉じない限り、cat
すべてが死ぬまで死ぬことはありません。
答え2
これは通常不可能です。あなたの特定のケースではこれが可能かもしれません。
バックグラウンドコマンドを実行した後、プロセスIDを取得できます。両親から。前景コマンド(メインプログラムのサブルーチン)を実行し、そのコマンドが順番にバックグラウンドコマンド(メインプログラムの孫)を実行する場合、メインプログラムは孫に対して直接的な可視性を持ちません。子プロセスを実行しています。これでプロセスが終了しました。
カーネルは子プロセス→親プロセス関係を追跡します。ps -o ppid= -p $pid
IDを持つプロセスの親プロセスのプロセスIDを表示するために実行できます$pid
。カーネルはプロセスの親プロセスを追跡しません。また、親プロセスが終了すると、そのプロセスはinit(プロセス番号1)によって採用されるため、その時点からその親プロセスIDは1になります。
追跡できるさまざまな継承されたプロセス属性があります。しかし、孫はこれらの属性のいずれかを離れることができます。孫がデーモンとして実行することを意図している場合は、対話型セッションに縛られず、関連する危険性がないように、できるだけ自分自身を隔離しようとすることができます(仲介者または孫自体から)。今回の会議で何が起こっているのか。
ps -o pgid=
現在のプロセス()と同じプロセスグループに属するプロセスを見つけることができます。これは同じプロセスグループで開始された別のプロセスをキャプチャしますが、逆に子プロセスまたはsetpgid
孫プロセスの呼び出しによって自分のプロセスグループで実行されている場合は孫プロセスを見逃しますsetpgrp
(デーモンが実行します)。
ps -o sid=
現在のプロセス()と同じセッションIDにあるプロセスを見つけることができます。これは同じセッションIDで開始された他のすべてのプロセスをキャプチャしますが、逆に自分のセッションで実行している場合は、子setsid
プロセスまたは孫プロセスの呼び出しが孫プロセスを見逃します(デーモンが実行します)。
一時ファイルを開き、ファイル( `fuser "$ tmpfile")を開いたプロセスを見つけることができます。これは、このファイルを開いた基本プロセス部分から開始されたプロセスのみをキャプチャーし、基本プロセスの他のコンポーネントが実行した可能性がある操作はキャプチャーしないため、より安定しています。ただし、他の解決策と同様に、そのプロセスまたは中間サブプロセスが未使用のファイル記述子を閉じると、孫プロセスは欠落しますが、デーモンプロセスはこれを行います。
ほとんどのデーモンには、フォアグラウンドにとどまるコマンドラインオプションがあります。それからそれを実行し、daemon --foreground & daemon_pid=$!
セッション終了時にデーモンが捕捉されないように予防措置を講じる必要があります(nohup daemon --foreground </dev/null >daemon.log 2>&1 &
これは良い開始です)。
答え3
strace
スクリプトを使用していくつかの情報を取得できますが、サブコマンドが完了するまで、straceは実行され続けます。例えば、
$ strace -b execve -e trace=none -e signal=none -f my-app
Process 21697 attached <-- is the () &
[pid 21696] +++ exited with 0 +++ <-- is my-app ending
Process 21698 attached <-- is the sleep 10
Process 21698 detached
test
Process 21702 attached <-- is the sleep 5
Process 21702 detached
+++ exited with 0 +++
echo
したがって、私たちはサブシェルのpidを持っており、組み込みシェルであると仮定して、コマンドがそこで実行されるので、追跡されません。-b execve
実行時に分離し、-f
子を追跡して-e
何も追跡しないことで出力を減らすことです。