例如:
$ 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
不这样做。当它被调用时,内核知道脚本的名称,然后执行env
。env
然后搜索$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