bash v3.2(尽管我认为也适用于较新的版本):
在 3.7.4 环境部分中,文档说:
在调用时,shell 会扫描自己的环境,并为找到的每个名称创建一个参数,自动将其标记为导出到子进程。
后来在附录 B 与 Bourne Shell 的主要区别中,文档说:
shell 初始环境中存在的变量会自动导出到子进程。 Bourne shell 通常不会执行此操作,除非使用导出命令显式标记变量。
我不明白这是什么意思。
下文中,cmd1.sh
包括
#!/bin/bash
echo yes $ben from cmd1
./cmd2.sh
并cmd2.sh
包括
#!/bin/bash
echo yes $ben from cmd2
我首先理解文档的意思是全部分配的变量将被导出(即不需要export
变量),即运行时
ben=you;
./cmd1.sh
我预计这会打印出来
yes you from cmd1
yes you from cmd2
但相反它打印
yes from cmd1
yes from cmd2
所以变量ben
似乎不会自动导出。然后我想文档可能意味着所有环境变量将被导出,即运行时
ben=you;
export ben;
./cmd1.sh
因为 cmd1 接收到一个环境变量ben
,然后ben
将自动导出,以便它在 cmd2 中可见。即我期望打印以下内容(实际上打印了以下内容):
yes you from cmd1
yes you from cmd2
然而,为了测试这是否与 Bourne shell 不同(如文档所述),我运行了完全相同的命令,但将 shebang 更改为指向/bin/sh
而不是/bin/bash
,并且我获得了完全相同的结果。即我没有看到任何区别。在 Bourne shell 中,我期望看到类似的输出
yes you from cmd1
yes from cmd2
任何人都可以帮助我理解文档在谈论“自动”标记导出参数时所指的内容,以及这与 Bourne shell 有何不同?
Nb 我确实发现了这关于 bash 和 bourne 中行为之间的具体差异的问题export
,但这似乎并不相关。
答案1
“shell 初始环境中存在的变量会自动导出到子进程。Bourne shell 通常不会执行此操作,除非使用导出命令显式标记变量。”。
考虑:
% export FOO=abc
% bash -c 'FOO=xyz; echo "bash: FOO=$FOO"; echo "env:"; env |grep FOO'
bash: FOO=xyz
env:
FOO=xyz
在这里,我在交互式 shell 中进行了设置FOO
(zsh,但这并不重要),并将其设置为导出。然后我运行 Bash,它在其环境中接收该变量,更改其值,打印它,然后运行env
.这是一个外部命令,因此它只能看到内部 Bash 显式传递给它的变量。我们看到 的修改值FOO
在内部 Bash 和 中可见,env
因此 Bash 确实从其环境中导入了该变量,并将其传递,就像传递任何导出的变量一样。
引用似乎描述的另一个行为是内壳不会将变量传递给env
,您会看到类似这样的内容:
bash: FOO=xyz
env:
我不知道所有历史实现的实际行为是什么,我所能重现的就是用 heirloom-sh (与 Kusalananda 提到的行为相同),只有原来的变量的值被传递:
% ./heirloom-sh -c 'FOO=xyz; echo "sh: FOO=$FOO"; echo "env:"; env |grep FOO'
sh: FOO=xyz
env:
FOO=abc
显式export FOO
内壳还将传递当前值。在这里,shell 确实使 的原始值FOO
对脚本可见,因此echo "FOO=$FOO"
第一件事也会打印FOO=abc
。
答案2
您不使用原始的 Bourne shell,因此您不会期望看到 Bourne shell 行为。 POSIX sh
shell(由系统上以 POSIX 模式运行的一个bash
或多个其他 shell 实现)的行为相同,并且在环境变量方面没有 Bourne shell 行为。dash
bash
在 POSIX shell 中,例如、sh
和当今 Unix 上的大多数其他常见 shell,shell 从 shell 的父进程继承的环境变量将传递给 shell 启动的任何子进程。附录中的文本将此称为“自动导出到子进程”的环境变量。bash
dash
附录声称原始 Bourne shell 中的情况并非如此,子进程继承的环境变量必须使用命令显式导出export
,无论它们是由 shell 从父环境继承的还是在其中创建的贝壳。
据我所知,最接近原始 Bourne shell 的 shell 是/usr/sunos/bin/sh
在 Solaris 上。这是就代码库而言,它实际上是原始的 Bourne shell,但多年来它可能进行了一些更新。
根据其手册,此 shell 的行为与文档附录中描述的行为不太一样bash
。它做将继承的环境变量传递给其子进程。但是如果您想修改继承的环境变量,则必须将它们导出以供子进程查看更改的值:
[...] 在调用时,shell 会扫描环境并为找到的每个名称创建一个参数,并为其提供相应的值。如果用户修改任何这些参数的值或创建新参数,则这些参数都不会影响环境,除非
export
使用该命令将 shell 的参数绑定到环境(也可以看看set –a
)。可以使用该命令从环境中删除参数unset
。因此,任何执行的命令所看到的环境都由 shell 最初继承的任何未修改的名称-值对组成,减去 删除的任何对unset
,加上任何修改或添加,所有这些都必须在export
命令中注明。
答案3
未导出的变量不在子进程的环境中。这意味着当您运行子 shell 时,它们不在该 shell 的初始环境中。
”shell 初始环境中存在的变量会自动导出到子进程" 是正确的,但如果变量尚未存在于环境中,则它们将无法用于该 shell 或重新导出到子 shell 的子级。
这几乎就是未导出的 shell 变量和导出的环境变量之间的区别。 shell 变量是当前 shell 的本地变量,导出的变量对当前 shell(或其他程序)及其子程序可见。
另外值得注意的是:子进程无法更改其父进程的环境...因此更改子进程中的环境变量对父进程没有影响。