bash 获取后台进程 id 给出父 pid

bash getting background process id gives parent pid

使用此命令创建 bash 脚本:

cat <<"END"> z
#! /bin/bash

sleep 20 && exit 1 &
ret=$!

ps $ret | grep $ret
END

然后 运行 它给出:

7230 pts/39   S+     0:00 /bin/bash ./z

我期待看到 sleep 20 ... 这是子进程。如果我删除 && exit 1 它会 return 子进程。

这是什么原因?如何获取上述语句中的子进程 ID?

你看到的不是父pid,而是子shell pid

当你 运行 :

sleep 20 && exit 1 &

进程树如下:

current-shell ---> sub-shell ---> 'sleep 20 && exit 1'

当你 运行 :

sleep 20 &

进程树如下:

current-shell ---> 'sleep 20'

您看到 'sleep 20'

的 pid 的原因

Whats the reason?

原因是某些实体必须做 &&。不可能是sleep,因为sleep只是休眠,在sleep终止后(所以不再有sleep做任何决定),一些"entity"需要比较sleep的退出状态,决定再执行exit 1。 "entity" 是 shell,必须是 "above" sleep 才能执行操作。所以 "real" 后台进程是 shell,sleep 是它的子进程。

在只有 sleep 20 & 的情况下,在 bash 中有一个优化,父 shell 在 bash 的情况下看到只有一个命令要做。所以 bash 扫描整个 command command bla bla & 并发现只有一个命令要做。因此 bash 只调用 exec 而不是标准的 fork+exec 并成为 sleep 本身而不是 运行 子进程。因为 exec subshell 变成了 sleep,所以你可以在进程名称中看到它。这是完成的资源优化 bash.

您已经获得了有关子进程的正确信息。只有在您的情况下,ps 不知道或不想为您在后台启动的链式子流程显示正确的 COMMAND 名称 - 这可能让您感到困惑。

看起来这是链式命令的情况(.. && ...,因此它与 exit 1 无关,也可能是 echo 5 等),其中 process group leader 名称显示为 cmd 名称。

来自 (ps man page)

`cmd | COMMAND`: simple name of executable

# Process state codes
`S`: interruptible sleep (waiting for an event to complete)
`+`: is in the foreground process group

查看 ps | grep 输出中的 S+

因此,您可以稍微调整一下脚本以确认您确实捕获了 (d) 有关子进程的正确信息,如下所示:

cat <<"END"> z
#! /bin/bash

sleep 20 && exit 1 &
ret=$!

echo $ret

jobs -l

# display parent and child process info
# -j Jobs format
ps -j  $$ $ret 
END

echo $ret 的输出:

30274

jobs -l 的输出:

[1]+ 30274 Running                 sleep 20 && exit 1 &

ps -j $$ $ret 的输出:

PID   PGID   SID   TTY    STAT    TIME COMMAND
30273 30273 21804 pts/0    S+     0:00 /bin/bash ./z
30274 30273 21804 pts/0    S+     0:00 /bin/bash ./z

注意父子进程都是一样的PGID,而jobs -l和[=31]显示的子进程的pid是30274 =] 匹配。

此外,如果您将 sleep 20 && exit 1 & 更改为 bash -c 'sleep 20 && exit 1' &,这次您将获得一个正确的子命令名称,如下所示(参见上面的输出顺序):

30384

[1]+ 30384 Running                 bash -c 'sleep 20 && exit 1' &

PID    PGID  SID   TTY    STAT    TIME COMMAND
30383 30383 21804 pts/0    S+     0:00 /bin/bash ./z
30384 30383 21804 pts/0    S+     0:00 bash -c sleep 20 && exit 1

最后但同样重要的是,在你的原始版本中你也可以尝试

而不是 ps $ret | grep $ret
pstree -s $ret

来自 pstree man page

-s: Show parent processes of the specified process.

这将为您提供类似于下面的输出,这也将确认您获得了 sleep 20 && exit 1 &:

的正确过程信息
systemd───systemd───gnome-terminal-───bash───bash───sleep