运行命令后
{ sleep 5; } &
的输出ps
是(输出1)
PID TTY TIME CMD
972 ttys000 0:00.27 -bash
2556 ttys000 0:00.00 -bash
2557 ttys000 0:00.00 sleep 5
同时为
( sleep 5 ) &
出的ps
是(输出 2)
PID TTY TIME CMD
972 ttys000 0:00.28 -bash
2566 ttys000 0:00.00 sleep 5
()
导致子shell环境,我期望在这种情况下“输出1”,因为它会导致分叉子进程,而我期望“输出2”,因为{ sleep 5; } &
它在当前shell中执行。这可能看起来是一个愚蠢的问题,但我真的不理解这种行为。
我在这里缺少什么?
答案1
Bash 将运行子 shell 的最后一个或唯一的命令和exec
如果它认为它可以安全地这样做,作为一种优化。您可以通过以下方式清楚地验证这一点pstree
:
$ pstree $$
bash---pstree
$ ( pstree $$ )
bash---pstree
$ ( pstree $$ ; echo)
bash---bash---pstree
$
这就是为什么你的( sleep 5 )
命令只显示为sleep
命令,而没有中间 shell。在上面的最后一个示例中,echo
强制 shell 在pstree
完成后执行某些操作,因此可以使用真正的 shell;在中间的情况下,生成的 shell 立即exec
s pstree
,因此它看起来与第一种情况相同(这是标准的分叉执行)。许多 shell 都这样做。
另一方面,任何事物在后台运行需要生成一个新进程:从根本上来说,这就是“背景”。大括号内的命令做通常在当前 shell 中运行,但如果它们在后台运行,则不会发生这种情况。为了让父 shell 可以在后台命令运行时继续执行它正在执行的操作,必须创建一个新的 shell 来运行整个 shell { ... }
。
$ { pstree $$ ; }
bash---pstree
$ { pstree $$ ; } &
bash---bash---pstree
在这种情况下,无论是否有另一个尾随命令都没有区别。Bash 记录了此行为&
:
如果命令由控制运算符“
&
”终止,则 shell 会在子 shell 中异步执行该命令。这称为在后台执行命令。 shell 不会等待命令完成,返回状态为 0 (true)。
您还可以通过后台看到这种情况发生一个内置命令,例如read
:
$ read &
$ pstree $$
[1] 32394
[1]+ Stopped read
$ pstree $$
bash-+-bash
`-pstree
我的外壳现在有二Children:bash
正在运行的read
,以及pstree
打印该输出的命令。
确实,shell 可以进行进一步的优化,并将其应用于后台,{ ... }
就像应用于带括号的子 shell 一样,但事实并非如此。其他一些 shell 可能会这样做,但我在快速测试中还没有找到。这是一个非常罕见的案例,并且形式上有所不同,并且存在一些奇怪的极端情况。