控制 bash 传递给子进程的环境

控制 bash 传递给子进程的环境

我正在使用 x86_64 GNU/Linuxbash

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    system("set > setc");                           // A subset of `$ set`

    return 0;
}

我可以看到文件 setc 包含$ set.
我很好奇外壳(父进程)如何决定向子进程提供什么以及不提供什么?
如果我想为子进程提供更多环境变量怎么办?怎样才能控制它呢?

答案1

shell 内置命令set显示所有变量,而不仅仅是那些已导出到环境中的变量。

如果您想向环境添加变量,只需export variablename在 shell 中执行即可。

答案2

在幕后,环境变量从一个程序传输到另一个程序的方式是通过execve系统调用,从磁盘加载新的程序映像。 (该图像替换了当前程序;还有另一个系统调用,fork,复制当前程序;函数(如system组合forkexecve以及一些其他系统调用来启动子进程。)在这个低级别,程序可以将其想要的任何内容传递给后继程序。

然而,大多数程序只是传递它们启动时收到的环境。库函数例如system读取初始化为程序初始环境的全局变量;这个全局变量可以被其他库函数修改,例如putenv.如果程序不包含专门设计用于更改环境的代码,它会将其环境传输到其子进程。

在您的测试中,您使用了 shell 命令set。此命令列出 shell 变量,它们是环境变量的超集。 shell将其所有环境变量公开为shell编程语言的变量;此外,您还可以定义更多变量。内置命令export将 shell 变量的名称作为参数并将该变量标记为已导出;标记为导出的变量将添加到子流程的环境中。

在交互式 shell 中,通常有许多未导出的 shell 变量。新启动的非交互式 shell 的数量较少。这就是为什么您看到的变量system("set")比在交互式 shell 中看到的变量少的原因。如果您查看环境变量(使用命令envexport不带参数),那么您会发现相同的变量(或者可能更多,具体取决于 shell)。

答案3

看完之后man bash有了全貌。如果我在发布问题之前读过 man 就好了!顺便说一句,上面的一个答案似乎不正确,而另一个答案则未能澄清。所以:

shell 有一个执行环境,它由shell 在调用时继承的打开文件、文件创建模式掩码、从环境中的 shell 父级继承的 shell 参数等组成。(更多详细信息“man bash命令执行环境”部分。)不带参数调用,吐出 shell 环境中存在的所有变量的值(以名称=值的形式)。 (请注意shell 环境的输出和组成部分。)declaresetsetdeclare

现在,当要执行除内置或 shell 函数之外的简单命令时,会在分离执行环境,由 shell 的打开文件、文件创建模式掩码、标记为导出的 shell 变量和函数(shell 环境中的变量和函数的子集)等组成。如果我们尝试分叉此命令,则正如 Gilles 在上面的答案中所说的那样“如果程序不包含专门设计用于更改环境的代码,它会将其环境传输到其子进程。”

因此,当我们尝试执行上述程序时,该程序会接收 shell 环境的子集。然后,当我们说 system("set > setrc") 时,system() 创建一个子进程(顺便说一句,它是 shell),然后子 shell 执行set > setrc。在执行此操作时,程序将其环境(父 shell 环境的简化版本)传输到子进程(shell 执行set)。现在,当set > setrc执行时,它会打印调用 shell 环境中存在的所有变量的值(以名称=值的形式),该环境是父 shell 环境的简化版本。这就是为什么减少了setrc的内容。

现在如何控制 Bash 传递给命令的环境:

我们可以借助 和 来控制 Bash 传递给命令的参数和export函数declare -x。 (man bash环境部分。)

正如其他人所提到的exportenv只是为了澄清:export并且env(不带参数)打印要传递给子进程(或命令)的环境,而不是 shell 的环境 whilesetdeclare吐出中的所有变量的环境。这就是为什么您可以 echo $VAR_NAME (任何存在于setdeclare输出中的变量,尽管它不在envexport输出中)。

相关内容