为什么通过命令替换调用传递到子 shell 的非环境变量?

为什么通过命令替换调用传递到子 shell 的非环境变量?

Bash 手册说:

命令替换、用括号分组的命令和异步命令在与 shell 环境重复的子 shell 环境中调用,不同之处在于 shell 捕获的陷阱将重置为 shell 在调用时从其父 shell 继承的值。

在此示例中,b不是环境变量,因此b不存在于通过命令替换创建的子 shell 中。那为什么要通过命令替换c来赋值呢?b是否因为参数扩展发生$b在创建子shell执行之前的shell进程中echo 1

$ b=1
$ c=$(echo $b)
$ echo $c
1

答案1

不,子 shell 是首先创建的。

Ashell执行环境包含由变量赋值和环境变量设置的 shell 参数。子shell环境是通过复制shell环境创建的,因此它包含当前shell环境的所有变量。

参见示例:

$ b=1
$ c=$(b=2; echo "$b")
$ echo "$c"
2

输出2不是1.


通过命令替换创建的子 shell 环境与通过调用 shell 可执行文件创建的 shell 环境不同。

当您将 shell 称为:

$ bash -c :

当前使用的 shell执行()创建新的 shell 进程,例如:

execve("/bin/bash", ["bash", "-c", ":"], [/* 64 vars */]) = 0

传递给的最后一个参数execve包含所有环境变量。

这就是为什么你需要出口将其推送到环境变量的变量,该变量将包含在后续执行的命令中:

$ a=; export a
$ strace -e execve bash -c :
execve("/bin/bash", ["bash", "-c", ":"], [/* 65 vars */]) = 0
+++ exited with 0 +++

请注意,环境变量从 64 更改为 65。并且未导出的变量将不会传递到新的 shell 环境:

$ a=; b=; export a
$ strace -e execve bash -c :
execve("/bin/bash", ["bash", "-c", ":"], [/* 65 vars */]) = 0
+++ exited with 0 +++

请注意,环境变量仍然是 65。


在命令替换中,shell 使用叉()创建新的 shell 进程,它只是复制当前的 shell 环境 - 其中包含变量集和环境变量。

答案2

是的 b 不是环境变量。
但是:是的,b确实存在于通过命令替换创建的子shell中:

$ b=11; c="$(echo $b)"; echo "$c"          ### b exists in subshell.
11
$ b=11; c="$(b=33; echo $b)"; echo "$c"    ### $b is not replaced before
33                                         ### the subshell is executed.

什么不接收变量是“完整子进程”:

$ b=11; bash -c 'echo "<$b>"'              ### b does not exist.
<>
$ b=11 bash -c 'echo "<$b>"'               ### environment b.
<11> 

当然,除了进程可以接收环境中的变量之外。


最后一行位于伍里奇子壳:

在子shell中,常规shell变量a是可见的;但因为它没有被导出,所以完整的子进程看不到它。

答案3

命令替换会导致对 shell 解释器的正常调用,并且该解释器将echosub-process.

sub-process是必需的,因为 shell 需要建立pipe命令echo以便能够读取结果。

sub-process运行该命令的是echo由创建的fork(),这会创建主 shell 进程中所有变量的副本。这就是为什么$b可以通过echo命令访问的原因。

相关内容