pgrepがenvを介して起動されたスクリプトを見つけることができないのはなぜですか?

pgrepがenvを介して起動されたスクリプトを見つけることができないのはなぜですか?

たとえば、

$ cat foo.sh
#!/usr/bin/env bash
while true; do sleep 1 ; done
$ ./foo.sh &
$ pgrep foo.sh
$

比較:

$ cat bar.sh
#!/bin/bash
while true; do sleep 1 ; done
$ ./bar.sh &
$ pgrep bar.sh
21202

次のコマンドで開始されたプロセスが出力env bashに表示されますps aux

terdon 4203  0.0  0.0  26676  6340 pts/3    S    17:23   0:00 /bin/bash

/bin/bashディスプレイの始まり

terdon  9374  0.0  0.0  12828  1392 pts/3    S    17:27   0:00 /bin/bash ./bar.sh

これがおそらく当初はキャプチャされていない理由を説明しているようですpgrep。したがって、質問は次のようになります。

  • 呼び出し時にスクリプト名が表示されないのはなぜですかenv
  • pgrep出力は単に解析されますかps
  • pgrep実行中のスクリプトを表示できるようにこの問題を解決する方法はありますかenv

答え1

質問1

envを介して呼び出すときにスクリプト名が表示されないのはなぜですか?

~から体本ウィキペディア記事:

Unixファミリーのオペレーティングシステムでは、shebangを持つスクリプトがプログラムとして実行されると、プログラムローダはスクリプトの最初の行の残りの部分をインタプリタコマンドで解析し、代わりに指定されたインタプリタを実行してスクリプトを実行しようとします。最初に使用されたパスはパラメータとして渡されます。

したがって、これが意味するのは、スクリプトの名前がカーネルに知られているプロセス名ですが、それを呼び出した直後にローダーが引数を実行し、残りのスクリプトを引数として渡すということ#!です。

しかし、envそうしないでください。呼び出されると、カーネルはスクリプト名を知ってそれを実行しますenvenv次に、$PATH実行する実行可能ファイルを検索します。

次にenvインタプリタを実行します。スクリプトの元の名前は知らず、カーネルだけを知っています。この時点で、envファイルの残りの部分が解析され、呼び出されたばかりのインタプリタに渡されます。

質問#2

pgrepは単にpsの出力を解析しますか?

はい、そうです。ps使用しているのと同じCライブラリを呼び出します。これは単なる包装紙以上ですps

質問#3

pgrepがenvを介して起動されたスクリプトを表示できるように、この問題を解決する方法はありますか?

出力に実行可能ファイルの名前を表示できますps

$ ps -eaf|grep 32405
saml     32405 24272  0 13:11 pts/27   00:00:00 bash ./foo.sh
saml     32440 32405  0 13:11 pts/27   00:00:00 sleep 1

この場合、pgrep -f <name>実行可能ファイルだけでなくコマンドライン引数全体を検索するので、それを使用して実行可能ファイルを見つけることができます。

$ pgrep -f foo
32405

引用する

答え2

pgrep出力を解析せずに、指定されたパターンに一致する項目をps検索し/proc/<PID>/statusて検索します。/proc/<PID>/cmdlineトレースを実行します。

getpid()                                = 6572
stat("/proc/self/task", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
openat(AT_FDCWD, "/proc", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
getdents(3, /* 267 entries */, 32768)   = 6792
stat("/proc/1", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
open("/proc/1/status", O_RDONLY)        = 4
read(4, "Name:\tinit\nState:\tS (sleeping)\nT"..., 1023) = 750
close(4)                                = 0
open("/proc/1/cmdline", O_RDONLY)       = 4
read(4, "/sbin/init", 2047)             = 10
close(4)                                = 0
stat("/proc/2", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
open("/proc/2/status", O_RDONLY)        = 4
read(4, "Name:\tkthreadd\nState:\tS (sleepin"..., 1023) = 518
close(4)                                = 0
open("/proc/2/cmdline", O_RDONLY)       = 4
read(4, "", 2047)                       = 0
close(4)                                = 0
stat("/proc/3", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
open("/proc/3/status", O_RDONLY)        = 4
read(4, "Name:\tksoftirqd/0\nState:\tS (slee"..., 1023) = 521
close(4)                                = 0
open("/proc/3/cmdline", O_RDONLY)       = 4
read(4, "", 2047)                       = 0
close(4)                                = 0
stat("/proc/6", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
open("/proc/6/status", O_RDONLY)        = 4
read(4, "Name:\tmigration/0\nState:\tS (slee"..., 1023) = 519
close(4)                                = 0
open("/proc/6/cmdline", O_RDONLY)       = 4
read(4, "", 2047)                       = 0
close(4)                                = 0

デフォルトでは、pgrepプロセス名のみが一致します(あなたの場合はbash)。からman pgrep

 -f     The pattern is normally only matched against the process name.
        When -f is set, the full command line is used.

したがって、あなたのケースで開始されたスクリプトを表示するには、env次を試してください。

pgrep -f foo.sh

関連情報