bash 与 bourne shell 中的导出命令行为

bash 与 bourne shell 中的导出命令行为

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 shshell(由系统上以 POSIX 模式运行的一个bash或多个其他 shell 实现)的行为相同,并且在环境变量方面没有 Bourne shell 行为。dashbash

在 POSIX shell 中,例如、sh和当今 Unix 上的大多数其他常见 shell,shell 从 shell 的父进程继承的环境变量将传递给 shell 启动的任何子进程。附录中的文本将此称为“自动导出到子进程”的环境变量。bashdash

附录声称原始 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(或其他程序)及其子程序可见。

另外值得注意的是:子进程无法更改其父进程的环境...因此更改子进程中的环境变量对父进程没有影响。

相关内容