bash 与 zsh 中子 shell 和 && 的不同行为

bash 与 zsh 中子 shell 和 && 的不同行为

我对在子 shell 中运行命令的理解是,当前 shell 被分叉,然后子 shell 进一步执行fork所需exec的命令。

在 bash 和 zsh 中运行以下命令时,我看到奇怪的行为:
$ ( sleep 5 && sleep 6 )

跑进去的时候巴什

$ echo $$
6410
$ ( sleep 5 && sleep 6 )

ps给出以下内容:
CMD:睡眠 5
PID:6590
生产者PID:6589

然后:
CMD: sleep 6
PID:6616
生产者PID:6589

这对我来说都是有道理的。sleep 5并且sleep 6两者具有相同的父级(大概是子shell)。

然而,在桀骜,我得到以下信息:

$ echo $$
1987
$ ( sleep 5 && sleep 6 )

ps给出以下内容:
CMD:睡眠 5
PID:7576
生产者PID:7575

然后:
CMD: sleep 6
PID:7575
生产者PID:1987年

我无法弄清楚为什么sleep 5用于运行的进程sleep 6作为其父进程,而用于运行的进程sleep 6将原始 shell 作为其父进程?

答案1

你可以在 Bash 中得到类似的东西:

$ echo $$
26328
$ ( /bin/sleep 1234 )
$ ps -o pid,ppid,stat,args -C sleep
  PID  PPID STAT COMMAND
26473 26328 S+   /bin/sleep 1234

Bash 在那里所做的是,虽然它确实为子 shell 分叉了自己,但它认识到只有一个命令可以在那里运行,然后只是 exec 本身sleep作为一种优化,从而节省了额外的分叉。对于类似的东西也有同样的作用bash -c '/bin/sleep'

只是 Bash 只针对一个命令执行此操作,使用( true; /bin/sleep 2345 ),您也会看到中间 shell 进程。显然 zsh 在优化方面更加积极,并且也跳过了列表中最后一个命令的 fork。


无论如何,不​​要求子 shell 在另一个 shell 中运行过程。只是它运行在另一个shell执行环境,来自POSIX shell 语言规范。现在,该环境确实包括诸如umask(操作系统级别上每个进程的内容)以及 shell 变量等,因此通过分叉实现单独的环境是简单的方式。但这不是一个要求。

例如 ksh 做了一些不同的事情,这个简单的子 shell 中根本没有分支。但它仍然有效,我们得到的值FOO是来自外壳层:

$ strace -etrace=clone,fork,vfork -f ksh -c 'FOO=out; ( FOO=in; true ); echo "$FOO"'
out
+++ exited with 0 +++

Bash 和 zsh 会在那里分叉。 ksh 也sleep作为内置实现,因此除非您显式使用,否则您不会在那里看到它的进程/bin/sleep

相关内容