编辑

编辑

我有兴趣从另一个 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 初始化之前调用,例如在输入 时。zshsetenv()gdbsetenv()main()

您会注意到bash's main()is int main(int argc, char* argv[], char* envp[])(并bash映射 中那些环境变量中的变量envp[]),而zsh's isint main(int argc, char* argv[])zsh从中获取变量environsetenv()确实修改environ但不能envp[]就地修改(在多个系统以及这些指针指向的字符串上只读)。

无论如何,在 shellenviron启动时读取后,使用setenv()将无效,因为 shell 此后不再使用environ(或getenv())。

相关内容