我有兴趣从另一个 shell 实例设置环境变量。所以我决定做一些研究。读完一篇后数字的问题关于这我决定测试一下。
我生成了两个 shell A 和 B (PID 420),都运行zsh
.从 shell AI 运行以下命令。
sudo gdb -p 420
(gdb) call setenv("FOO", "bar", 1)
(gdb) detach
当我运行时,从 shell B 中env
我可以看到变量 FOO 确实设置了值 bar。这让我认为 FOO 已经在 shell B 的环境中成功初始化。但是,如果我尝试打印 FOO,我会得到一个空行,表明它尚未设置。对我来说,感觉这里有一个矛盾。
这在我自己的 Arch GNU/Linux 系统和 Ubuntu VM 上进行了测试。我还在bash
变量甚至没有出现在 env 中的地方测试了这一点。虽然这让我感到失望,但如果 shell 在生成时缓存其环境的副本并且仅使用它(这是在链接的问题之一中建议的),那么这是有意义的。这仍然没有回答为什么zsh
可以看到变量。
为什么输出为echo $FOO
空?
编辑
在评论中输入后,我决定做更多测试。结果如下表所示。第一列是FOO
变量被注入的 shell。第一行包含命令,其输出可以在其下方看到。该变量FOO
是使用以下方式注入的sudo gdb -p 420 -batch -ex 'call setenv("FOO", "bar", 1)'
:特定于 zsh: 的命令zsh -c '...'
也使用 bash 进行了测试。结果是相同的,为简洁起见,省略了它们的输出。
Arch GNU/Linux、zsh 5.3.1、bash 4.4.12(1)
| | env | grep FOO | echo $FOO | zsh -c 'env | grep FOO' | zsh -c 'echo $FOO' | After export FOO |
|------|------------------|-----------|---------------------------|----------------------|-----------------------------------|
| zsh | FOO=bar | | FOO=bar | bar | No Change |
| bash | | bar | | | Value of FOO visible in all tests |
Ubuntu 16.04.2 LTS、zsh 5.1.1、bash 4.3.48(1)
| | env | grep FOO | echo $FOO | zsh -c 'env | grep FOO' | zsh -c 'echo $FOO' | After export FOO |
|------|------------------|-----------|---------------------------|----------------------|-----------------------------------|
| zsh | FOO=bar | | FOO=bar | bar | No Change |
| bash | | bar | | | Value of FOO visible in all tests |
上述似乎暗示结果与分布无关。这并没有告诉我更多的信息zsh
,只是bash
以不同的方式处理变量的设置。此外,export FOO
根据 shell 的不同,在此上下文中具有非常不同的行为。希望这些测试能让其他人明白一些事情。
答案1
大多数 shell 不使用getenv()
// setenv()
API putenv()
。
启动时,他们为收到的每个环境变量创建 shell 变量。这些将存储在需要携带其他信息(例如变量是否导出、只读)的内部结构中……他们不能使用 libc 来environ
实现这一点。
同样,出于这个原因,他们不会使用execlp()
,execvp()
来执行命令,而是直接调用系统调用,根据导出的变量列表execve()
计算数组。envp[]
因此,在您的 中gdb
,您需要向 shell 内部变量表添加一个条目,或者可能调用正确的函数,使其解释代码export VAR=value
以自行更新该表。
至于为什么您在调用 和 时看到差异,我bash
怀疑这是因为您在 shell 初始化之前调用,例如在输入 时。zsh
setenv()
gdb
setenv()
main()
您会注意到bash
's main()
is int main(int argc, char* argv[], char* envp[])
(并bash
映射 中那些环境变量中的变量envp[]
),而zsh
's isint main(int argc, char* argv[])
并zsh
从中获取变量environ
。setenv()
确实修改environ
但不能envp[]
就地修改(在多个系统以及这些指针指向的字符串上只读)。
无论如何,在 shellenviron
启动时读取后,使用setenv()
将无效,因为 shell 此后不再使用environ
(或getenv()
)。