规格
根据这个在线 POSIX 规范,在 Shell & Utilities, Shell Command Language, Section 2.9.3 Lists 中,有关异步列表的内容如下:
当异步列表的一个元素(列表中以 <&> 结尾的部分,例如命令1,上面)由 shell 启动,异步列表元素中最后一个命令的进程 ID 在当前 shell 执行环境中应该是已知的;请参阅 Shell 执行环境。该进程 ID 应保持已知状态,直到:
命令终止,应用程序等待进程 ID。
$!
在当前执行环境中展开“ ”(对应上一个异步列表)之前,会调用另一个异步列表。该实现无需在当前 shell 执行环境中的已知进程 ID 列表中保留超过 {CHILD_MAX} 个最新条目。
其他相关引述:
- 从同上,第 2.12 节 Shell 执行环境:
shell执行环境由以下部分组成:
[…]
- 该 shell 环境已知的异步列表中最后命令的进程 ID;请参阅异步列表
- 从 Shell & Utilities、Utilities,请等待:
当 shell 启动异步列表(请参阅异步列表)时,异步列表的每个元素中的最后一个命令的进程 ID 在当前 shell 执行环境中应为已知;请参阅 Shell 执行环境。
[…]
如果在没有操作数的情况下调用等待实用程序,则它将等待,直到调用 shell 已知的所有进程 ID 都已终止并以零退出状态退出。
[…]
已知进程 ID 仅适用于当前 shell 执行环境中的 wait 调用。
困惑
在 Bash 中,help wait
指定“所有当前活动的子进程”。
该规范仅指定所有已知的进程 ID,并且调用另一个异步列表似乎会导致进程 ID 被“遗忘”。
如果我正确解释了 POSIX 规范,那么以下程序将只等待 5 秒:
#! /bin/sh
sleep 10 &
sleep 5 &
wait
$!
由于之前没有出现过sleep 5 &
,是不是IDsleep 10
忘记了,没有等待?
同样,在两行之间插入: $!
也不会被忘记,对吗?
让我尝试更清楚地阐述我的逻辑:
- 规格:已知的过程。 ID 是 shell 执行环境的一部分。让我们想象这是一些列表,因为实现细节并不重要。
- SPEC:运行
command &
会导致过程。该异步的 IDcommand
为“已知”(IE,在列表中)。- SPEC(可疑,但请参阅第一个引用):运行
command2 &
而不以某种方式扩展$!
会导致 proc.上一个命令的 ID 变为不再为人所知! (但现在已知 proc.ID。command2
)
- SPEC(可疑,但请参阅第一个引用):运行
- SPEC:
wait
不带参数仅使用已知的过程。身份证。 - 结论:因此,不强制
$!
异步命令之间的扩展会导致wait
忘记等待某些子进程。
有些人认为wait
等待所有进程。规范中提到“已知”进程,它有具体的定义。有些人认为“已知”指的是“in $!”即使规范没有说这样的事情(此外,“已知进程 ID”是复数数量,而$!
不是)。
我同意这没有实际意义;当我开始新的工作时,shell 不会忘记我的工作。那么我在哪里误读了规范呢?
问题
- POSIX 实际上是否需要使用
$!
for 来使wait
不带参数的行为合理,例如在循环中启动异步列表时? - 是否有任何 shell(POSIX 或其他)实际上以这种方式实现规范(IE,实际上,我可以避免添加到
: $! expand async. proc. ID to prevent forgetting it
使用 nullary 的程序中吗wait
?
答案1
当您启动两个后台作业时,$!
首先设置为第一个作业的 PID,然后设置为第二个作业的 PID。这并不意味着 shell 失去了对第一个作业的跟踪。 shell 也并不真正关心你用什么来做什么$!
。
请注意标准文本是如何表示wait
不带任何参数的将等待全部后台工作?这与启动的后台作业数量以及您对该$!
参数执行或不执行的操作无关。
您显示的示例中,两个睡眠调用在后台运行,保证等待两个调用终止,这意味着调用wait
返回需要 10 秒。