【问题标题】:bash getting background process id gives parent pidbash 获取后台进程 id 提供父 pid
【发布时间】:2020-03-31 20:53:21
【问题描述】:

使用此命令创建 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 ... 这是子进程。如果我删除 &amp;&amp; exit 1 它确实会返回子进程。

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

【问题讨论】:

  • 我认为命令显示的是父进程的名称,因为命令比较复杂,无法生成名称。
  • @dan1st 好吧……这对我来说没有意义。不管 ps $ret | grep $ret 我实际上检查了返回 pid 并与父子 pid 进行了比较。它实际上返回父 pid
  • man bashAND and OR lists are sequences of one or more pipelines separated by the &amp;&amp; and || control operators, respectively. 对于您的问题,我看不出这意味着什么,但我们同意这不是微不足道的。

标签: bash pid


【解决方案1】:

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

看起来这是链式命令的情况(.. &amp;&amp; ...,因此它与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 -lps ...显示的子进程的pid 30274匹配。

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

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:显示指定进程的父进程。

这将为您提供类似于下面的输出,这也将确认您获得了sleep 20 &amp;&amp; exit 1 &amp; 的正确进程信息:

systemd───systemd───gnome-terminal-───bash───bash───sleep

【讨论】:

    【解决方案2】:

    什么原因?

    原因是某些实体必须做&amp;&amp;。不可能是sleep,因为sleep只是休眠,而sleep终止后(所以不再有sleep做任何决定),一些“实体”需要比较@987654326的退出状态@并决定然后执行exit 1。该“实体”是外壳,必须在“上方”sleep 才能执行操作。所以“真正的”后台进程是shell,sleep是它的子进程。

    在只有sleep 20 &amp; 的情况下,bash 中有一个优化,如果 bash 看到只有一个命令可以执行,则父 shell 会这样做。所以 bash 扫描整个command command bla bla &amp; 并看到只有一个命令可以执行。因为那个 bash 只调用exec 而不是标准的fork+exec 并成为sleep 本身而不是运行子进程。因为exec 子shell 变成sleep,所以你可以在进程名称中看到它。这是一个资源优化完成的 bash。

    【讨论】:

    • 感谢您的回答。这很有帮助。我决定选择另一个更完整的答案
    【解决方案3】:

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

    当你运行时:

    sleep 20 && exit 1 &
    

    进程树是这样的:

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

    当你运行时:

    sleep 20 &
    

    进程树是这样的:

    current-shell ---> 'sleep 20'
    

    您看到 pid 表示“睡眠 20”的原因

    【讨论】:

    • 感谢您的回答。这很有帮助。我决定选择另一个更完整的答案
    猜你喜欢
    • 2018-05-21
    • 1970-01-01
    • 2020-03-24
    • 2017-01-23
    • 2014-06-25
    • 2011-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多