如果进程继承了父进程的环境,为什么我们需要导出?

如果进程继承了父进程的环境,为什么我们需要导出?

我读这里shell 中的目的export是使变量可用于从 shell 启动的子进程。

不过,我也读过这里这里“进程从其父进程(启动它们的进程)继承其环境。”

如果是这样的话,我们为什么需要export?我缺少什么?

默认情况下,shell 变量不是环境的一部分吗?有什么不同?

答案1

您的假设是 shell 变量是在环境中。这是不正确的。该export命令定义了环境中的名称。因此:

a=1 b=2
export b

结果当前知道它$a会扩展到 1 和$b2,但子进程不会知道任何事情,a因为它不是环境的一部分(即使在当前 shell 中)。

一些有用的工具:

  • set:用于查看当前 shell 的参数(导出或未导出)
  • set -k: 套指定的参数在环境中。考虑f() { set -k; env; }; f a=1
  • set -a:告诉 shell 将设置的任何名称放入环境中。就像放在export每项作业之前一样。对于.env文件很有用,如set -a; . .env; set +a.
  • export:告诉 shell 在环境中输入一个名称。导出和赋值是两种完全不同的操作。
  • env:作为外部命令,env只能告诉你有关遗传环境,因此,它对于健全性检查很有用。
  • env -i:对于在启动子进程之前清除环境很有用。

替代方案export

  1. name=val command# 命令之前的赋值将该名称导出到命令。
  2. declare/local -x name# 导出名称,当您想避免将名称暴露给外部作用域时,在 shell 函数中特别有用。
  3. set -a# 导出以下每个作业。

动机

那么为什么 shell 需要有自己不同的变量和环境呢?我确信有一些历史原因,但我认为主要原因是范围界定。该环境适用于子进程,但是您可以在 shell 中执行许多操作,而无需分叉子进程。假设你循环:

for i in {0..50}; do
    somecommand
done

somecommand为什么要通过包含来浪费内存i,使其环境比需要的更大?如果您在 shell 中选择的变量名称恰好意味着程序无意的含义怎么办? (我个人最喜欢的包括DEBUGVERBOSE。这些名称随处可见,但命名空间很少。)

如果不是shell,那是什么环境呢?

有时要了解 Unix 行为,您必须查看系统调用,这是与内核和操作系统交互的基本 API。在这里,我们正在研究exec调用系列,这是 shell 在创建子进程时使用的调用系列。这是来自联机帮助页exec(3)(强调我的):

execle()和函数execvpe()允许调用者通过参数 envp 指定执行程序的环境。 envp 参数是指向以 NULL 结尾的字符串的指针数组,并且必须以 NULL 指针结尾。其他函数从调用进程中的外部变量 environ 获取新进程映像的环境。

因此,在 shell 中编写export somename相当于将名称复制到environC 中的全局字典中。但是somename在不导出它的情况下进行赋值就像在 C 中对其进行赋值一样,而不将其复制到变量中environ

答案2

shell 变量和环境变量之间是有区别的。如果定义 shell 变量而不对其export进行 ing,则它不会添加到进程环境中,因此不会继承到其子进程。

使用export您告诉 shell 将 shell 变量添加到环境中。您可以使用测试它printenv(它只是将其环境打印到stdout,因为它是一个子进程,您可以看到exporting 变量的效果):

#!/bin/sh

MYVAR="my cool variable"

echo "Without export:"
printenv | grep MYVAR

echo "With export:"
export MYVAR
printenv | grep MYVAR

答案3

变量一旦导出,就是环境的一部分。PATH在 shell 本身中导出,而自定义变量可以根据需要导出。使用一些设置代码:

$ cat subshell.sh 
#!/usr/bin/env bash
declare | grep -e '^PATH=' -e '^foo='

比较

$ cat test.sh 
#!/usr/bin/env bash
export PATH=/bin
export foo=bar
declare | grep -e '^PATH=' -e '^foo='
./subshell.sh
$ ./test.sh 
PATH=/bin
foo=bar
PATH=/bin
foo=bar

$ cat test2.sh 
#!/usr/bin/env bash
PATH=/bin
foo=bar
declare | grep -e '^PATH=' -e '^foo='
./subshell.sh
$ ./test2.sh 
PATH=/bin
foo=bar
PATH=/bin

由于foo不是由 shell 导出的,并且test2.sh从未导出它,因此它不是subshell.sh上次运行环境的一部分。

答案4

总之:

  • 进程确实从其父进程继承环境变量
  • 在 bash 中, $a=1 创建一个“普通”变量,而不是环境变量
  • export a=1 为终端的进程创建一个环境变量(还有其他方法可以做到)
  • 还有其他方法可以将环境数据传递给子进程

相关内容