交互式sh
shell 会话:
$ sh
$ timeout 1 yes | sed -n s/a/b/p ; echo $?
Terminated
143
$
非交互式脚本通过sh -c
:
$ sh -c 'timeout 1 yes | sed -n s/a/b/p ; echo $?'
0
$
为什么这两个示例会产生不同的退出代码?
交互式sh
shell 会话:
$ sh
$ timeout 1 yes | sed -n s/a/b/p ; echo $?
Terminated
143
$
非交互式脚本通过sh -c
:
$ sh -c 'timeout 1 yes | sed -n s/a/b/p ; echo $?'
0
$
为什么这两个示例会产生不同的退出代码?
timeout
timeout
(至少GNU )默认情况下尝试在新进程组中运行该命令,并在超时时使用 SIGTERM 终止该进程组。这样,如果该命令生成更多进程,它们也会在超时时被终止。
请参阅
timeout
执行 asetpgid(0, 0)
(与 相同setpgrp()
)来创建一个新的进程组,您会看到它正在执行kill(316060, SIGTERM)
Killsleep 2
,而且还kill(0, SIGTERM)
杀死了它自己的进程组(它之前创建的),这也会杀死由它生成的进程(sleep 2
如果有)(在本例中没有) )。现在,当在 shell 中启用作业控制时(例如交互或使用
-m
/-o monitor
选项调用时),在:shell 已经启动
timeout
并sleep 3
在一个新的进程组中,并且在大多数 shell 中,进程组领导者将是运行的进程组timeout
。因此,当
timeout
执行时setpgrp()
,与 相同setpgid(0, 0)
,它不会创建新的进程组,而只是成为空操作。因此,进程组仍将包含timeout
它生成的要运行的进程sleep 2
,并且sleep 3
该进程是由 shell 预先放置在那里的。这次,它
kill(0, SIGTERM)
会终止正在运行的进程sleep 3
,因为它也在 318594 进程组中(由 shell 使用 放置在那里setpgid(318595, 318594)
)。在:
你会发现管道持续了 4 秒,仅
sleep 3
在 1 秒后被杀死,因为此时 shell 创建的进程组是由正在运行的进程引导的sleep 2
,因此timeout
能够为自己(及其子进程)创建一个新的进程组sleep 3
)这与 shell 为管道中的 3 个命令创建的进程组不同(因此,您会发现Ctrl+C或Ctrl+在运行时Z无法正常工作,因为未在前台进程组中运行)timeout
timeout
当使用 运行时
--foreground
,timeout
会跳过创建额外的进程组,并且不会执行kill(0, SIGTERM)
杀死自己的进程组的操作,因此行为更加一致,但意味着孙进程不会被杀死。sh
被杀了,但没有sleep 2
。这也解释了为什么在终端的交互式 shell 中:
或者:
cat
无法从终端读取。当它尝试时最终会被挂起,因为它位于新的进程组中,因此不再位于终端的前台进程组中。同样,添加该
--foreground
选项可以避免该问题。