Por que o pgrep não consegue encontrar scripts iniciados via env?

3

Por exemplo:

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

Compare com:

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

O processo iniciado por env bash aparece na saída de ps aux as:

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

enquanto o começou com /bin/bash mostra como

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

que provavelmente explica porque o primeiro não está sendo capturado por pgrep . Então, as perguntas são:

  • Por que o nome do script não é exibido quando chamado por env ?
  • O pgrep simplesmente analisa a saída de ps ?
  • Existe alguma maneira de contornar isso para que pgrep possa me mostrar scripts iniciados por meio de env ?
por terdon 06.08.2013 / 17:28

2 respostas

2

Q # 1

Why does the name of the script not show up when called through env?

Do artigo da wikipedia :

Under Unix-like operating systems, when a script with a shebang is run as a program, the program loader parses the rest of the script's initial line as an interpreter directive; the specified interpreter program is run instead, passing to it as an argument the path that was initially used when attempting to run the script.

Portanto, isso significa que o nome do script é conhecido pelo kernel como o nome do processo, mas imediatamente após ele ser chamado, o carregador então executa o argumento para #! e passa o resto do script em como um argumento.

No entanto, env não faz isso. Quando é chamado, o Kernel sabe o nome do script e, em seguida, executa env . env procura o $PATH procurando o executável para exec.

É então env que executa o interpretador. Não sabe nada do nome original do script, apenas o Kernel sabe disso. Neste ponto env está analisando o restante do arquivo e passando para o interpretador que acabou de invocar.

Q # 2

Does pgrep simply parse the output of ps?

Sim, mais ou menos. Ele está chamando as mesmas bibliotecas C que o ps está usando. Não é simplesmente um wrapper em torno de ps .

Q # 3

Is there any way around this so that pgrep can show me scripts started via env?

Eu posso ver o nome do executável na saída 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

Nesse caso, você pode usar pgrep -f <name> para encontrar o executável, pois ele pesquisará todo o argumento da linha de comando, não apenas o executável.

$ pgrep -f foo
32405

Referências

por 06.08.2013 / 18:12
3

pgrep não analisa a saída de ps , ele procurará /proc/<PID>/status e /proc/<PID>/cmdline até encontrar correspondência para determinado padrão. Faça algum strace:

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

Por padrão, pgrep corresponde apenas ao nome do processo (que é o bash no seu caso). De man pgrep :

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

Portanto, no seu caso, se você quiser mostrar o script iniciado via env , tente usar:

pgrep -f foo.sh
    
por 06.08.2013 / 18:46