子 shell 创建和处理

子 shell 创建和处理

我很难很好地理解 Bash 管理子 shell 创建的方式以及相关的范围问题。希望有人能让我对这个问题的想法保持一致。

我不明白的第一件事是子 shell 中处理变量的方式。我认为变量不会被继承,除非它们打开了 eXport 标志。然而,对于通过分组创建的子 shell 来说,情况似乎并非如此:

$ n=3
$ ( pstree $$ ; echo $n )
bash---bash---pstree
3

相反,如果我手动创建子 shell,一切都会按预期进行:

$ export ppid=$$
$ bash
$ pstree $ppid
bash---bash---pstree
$ echo $n

另外,有些分组实际上并没有创建子 shell:

( pstree $$ )
bash---pstree

一些不应该的分组,实际上却是:

$ pstree $$ &
[1] 1685
$ bash---pstree
{ pstree $$; } &
[1] 1687
$ bash---bash---pstree

这对我来说似乎有点混乱,有很多特殊情况需要跟踪。而且它变得更加混乱。考虑流程替换:

$ cat <(pstree $$)
bash-+-bash---pstree
    `-cat

在这里,根据我的理解,bash 在子进程中执行 cat,给它一个 FIFO 来读取。然后分叉一个子 shell,为其提供 FIFO 的另一端。子 shell 运行 pstree。

现在考虑:

$ pstree $$ > >(cat)  # There is some non determinism involved, output may be different
bash---pstree---bash---cat
#or sometimes
bash---pstree---bash

这里 bash 似乎做了一些不同的事情。它fork了一个子shell,子shell又fork了另一个子shell,情况就变成:bash---bash(1)---bash(2)

bash(1)(获取 FIFO 的写入端)执行 pstree,bash(2)(获取 FIFO 的写入端)在子进程中运行 cat。

因此,在第一种情况下,子进程由主 shell 的子 shell 执行。第二个由主命令的 shell 子进程执行。

在我看来,第二种情况是因为 pstree 可能在创建 cat 之前运行。

答案1

我将尝试涵盖其中的大部分内容。

当您执行子 shell 时()$(), <(): bash 将调用fork().这将创建一个新的子进程,即一模一样作为父级,除了pidppid和 fork 的返回值(0 表示子级;正 pid,子级表示父级;负数表示错误 - 未创建子级)。因此,子 shell 将具有相同的状态/相同的变量。

当你调用 bash 时,shell 会调用fork()then 它会调用exec("bash")(实际上是变体之一)。这会将克隆的 bash 替换为新映像,并从头开始运行。因此变量被清除,配置文件被重新读取。

当你使用&. Bash 调用fork()的是后台进程。看起来它比需要的次数多了一次分叉,但可能正在使用分叉的 bash 来帮助管理工作。 (为什么不分叉是很便宜的)

对于最后一种情况pstree $$ > >(cat)。 (这花了我更长的时间来解决):
Bash 分叉,一如既往,运行一个新进程。在调用 exec 之前,它需要重定向 stdin/out/err (在本例中只是 stdout)。为此,它必须cat在子 shell 中运行,所以它确实如此。现在cat是新狂欢的孩子。新的狂欢是旧的孩子。接下来 new-bash 调用exec,并变为pstree

相关内容