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 解释器的正常调用,并且该解释器将echo
在sub-process
.
这sub-process
是必需的,因为 shell 需要建立pipe
命令echo
以便能够读取结果。
sub-process
运行该命令的是echo
由创建的fork()
,这会创建主 shell 进程中所有变量的副本。这就是为什么$b
可以通过echo
命令访问的原因。