获取管道后台进程 PID 的可靠方法

获取管道后台进程 PID 的可靠方法

我需要检索一个进程的 PID,该进程通过管道传输到另一个进程,这些进程一起作为 bash 中的后台作业生成。以前我只是简单地依赖pgrep,但事实证明,在pgrep找到进程之前可能会有 >2 秒的延迟:

#!/bin/bash
cmd1 | cmd2 &
pid=$(pgrep cmd1) # emtpy in about 1/10

我发现针对此问题的一些常见建议是使用进程替换而不是简单的管道 ( cmd1 >(cmd2) & pid=$!) 或使用jobs内置函数。进程替换运行整个子shell(对于整个运行时),所以我宁愿jobs现在使用,但我想避免两次犯同样的错误......

如果我在生成两个进程后立即执行查找,我是否可以 100% 依赖于jobs了解这两个进程?

#!/bin/bash
cmd1 | cmd2 &
pid=$(jobs -p %cmd1) # 10/10?

这可能是由于在后台运行作业,或者可能是一个怪癖set -x,但以下示例通常以任意顺序列出执行的命令。输出jobs出现到目前为止是正确的,但我想完全排除可能jobs被执行的可能性作业已经启动(或者至少jobs无法列出两个进程)!?

#!/bin/bash
set -x
tail -f /dev/null | cat &
jobs -l
kill %tail

例子:

+ jobs -l
[1]+ 2802325 Running                 tail -f /dev/null
     2802326                       | cat &
+ tail -f /dev/null
+ kill %tail

同样,在流程替换的情况下,我可以依赖pid=$!始终工作吗?它到底被设计为“扩展到最近执行的后台(异步)命令的进程ID”?

答案1

当后台作业是表单的管道时cmd1 | cmd2,它仍然是单个后台作业。没有办法知道什么时候cmd1开始。

每个&都会创建一个后台作业。一旦cmd &返回,shell 就会知道该后台作业:cmd & jobslists cmdcmd & pid=$!设置pid为运行的进程 ID cmd

该管道cmd1 | cmd2创建了另外两个子进程:一个 to run cmd1,一个 to run cmd2。这两个进程都是运行后台作业的子进程的子进程。以下是进程树的样子bash -c '{ sleep 123 | sleep 456; } & jobs -p; sleep 789'

 PID PPID CMD
 268  265  |   \_ bash -c { sleep 123 | sleep 456; } & sleep 789
 269  268  |       \_ bash -c { sleep 123 | sleep 456; } & sleep 789
 270  269  |       |   \_ sleep 123
 271  269  |       |   \_ sleep 456
 272  268  |       \_ sleep 789

268是原始的bash进程。 269 是打印的后台作业jobs -p。 270 和 271 是管道的左侧和右侧,都是后台作业 (269) 主进程的子进程。

我测试的 bash 版本(Linux 上为 5.0.17)cmd1 | cmd2 &在没有大括号的情况下进行了优化。在这种情况下,管道的左侧与后台作业在同一进程中运行:

 PID PPID CMD
 392  389  |   \_ bash -c sleep 123 | sleep 456 & jobs -p; sleep 789
 393  392  |       \_ sleep 123
 394  392  |       \_ sleep 456
 395  392  |       \_ sleep 789

您不能依赖此行为在 bash 版本之间保持稳定,甚至可能在平台、发行版、libc 版本等之间保持稳定。

jobs -p %cmd1寻找代码以 开头的工作cmd1。它发现的是cmd1 | cmd2.jobs -p %?cmd2找到同样的工作。无法通过 bash 的内置功能cmd1访问正在运行的进程 ID。cmd2

如果您需要确定是否cmd1已启动,请使用进程替换。

cmd1 >(cmd2)

你不知道什么时候cmd2开始和结束。

如果您需要知道何时开始cmd1cmd2结束,则需要使它们同时工作,并让它们通过命名管道进行通信。

tmp=$(mktemp -d) # Remove this in cleanup code
mkfifo "$tmp/pipe"
cmd1 >"$tmp/pipe" & pid1=$!
cmd2 <"$tmp/pipe" & pid2=$!

jobs命令在脚本中不是很有用。用于$!记住后台作业的 PID。

1或者至少应该如此。我的版本抱怨工作规范不明确,这一定是一个错误,因为它说尽管只有一个工作。

相关内容