我需要检索通过管道传输到另一个进程的进程的 PID,这些进程一起作为 bash 中的后台作业生成。以前我只是依靠pgrep
,但事实证明在pgrep
能够找到该过程之前可能会有> 2s的延迟:
#!/bin/bash
cmd1 | cmd2 &
pid=$(pgrep cmd1) # emtpy in about 1/10
我发现针对这个问题的一些常见建议是使用进程替换而不是简单的管道 ( cmd1 >(cmd2) & pid=$!
) 或使用jobs
内置函数。进程替换运行整个子shell(对于整个运行时),所以我现在宁愿使用jobs
,但我想避免两次犯同样的错误......
jobs
如果我在生成它们后立即执行查找,我可以 100% 依赖于了解这两个进程吗?
#!/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”而设计的?
当后台作业是表单的管道时
cmd1 | cmd2
,它仍然是单个后台作业。没有办法知道什么时候cmd1
开始。每个
&
创建一个后台作业。一旦cmd &
返回,shell 就会知道该后台作业:cmd & jobs
listscmd
。cmd & pid=$!
设置pid
为运行的进程 IDcmd
。管道
cmd1 | cmd2
创建了另外两个子流程:一个要运行cmd1
,一个要运行cmd2
。这两个进程都是运行后台作业的子进程的子进程。以下是进程树的样子bash -c '{ sleep 123 | sleep 456; } & jobs -p; sleep 789'
:268 是原始的 bash 进程。269 是
jobs -p
打印的后台作业。270 和 271 是管道的左侧和右侧,它们都是后台作业 (269) 主进程的子进程。我测试的 bash 版本(Linux 上为 5.0.17)优化了
cmd1 | cmd2 &
没有大括号。在这种情况下,管道的左侧与后台作业在同一进程中运行:您不能依赖这种行为在 bash 版本之间保持稳定,甚至可能跨平台、发行版、libc 版本等。
jobs -p %cmd1
查找代码以 . 开头的作业cmd1
。它发现的是cmd1 | cmd2
.jobs -p %?cmd2
找到相同的工作¹。无法通过 bash 的内置功能访问正在运行的进程 IDcmd1
。cmd2
如果您需要确定
cmd1
已经开始,请使用进程替换。你不知道什么时候
cmd2
开始和结束。如果您需要知道何时
cmd1
、cmd2
开始和结束,您需要让它们同时工作,并让它们通过命名管道进行通信。该
jobs
命令在脚本中不是很有用。用于$!
记住后台作业的 PID。¹或者至少应该如此。我的版本抱怨一个模棱两可的工作规范,这必须是一个错误,因为它说尽管只有一个工作。