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

Ame*_*ina 94 shell process environment-variables

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

但是,我也看到了这里,并在这里的“进程从它们的父(这开始它们的进程)继承他们的环境。”

如果是这种情况,我们为什么需要export?我错过了什么?

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

koj*_*iro 106

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

a=1 b=2
export b
Run Code Online (Sandbox Code Playgroud)

导致当前shell知道$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
Run Code Online (Sandbox Code Playgroud)

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

如果不是外壳,环境是什么?

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

execle()execvpe()功能允许呼叫者通过参数envp指定执行程序的环境。envp 参数是一个指向空终止字符串的指针数组,必须以空指针终止。其他函数从调用进程中的外部变量environ 获取新进程映像的环境。

所以export somename在shell中写就相当于把名字复制到environC中的全局字典中。但是赋值somename而不导出就像在C中赋值一样,而不是将它复制到environ变量中。

  • `set -k` 这样就可以使用 `cmd ENVVAR=value` 代替 `ENVVAR=value cmd`,除非在调用 `f` 之前运行 `set -k`,否则在你的示例中将不起作用。此外,现在支持它的 shell 并不多,只是为了与 Bourne shell 向后兼容。在 Bourne(或 Korn)shell 中,这不适用于函数。并且因为它会影响 shell 解析,所以它必须在 shell _读取_在那里使用它的代码时生效。 (3认同)
  • @mblakesley 好的,我又试了一次。 (2认同)

And*_*ese 29

shell 变量和环境变量之间存在差异。如果你定义一个 shell 变量而不对其进行exporting,它不会被添加到进程环境中,因此不会被继承到它的子进程。

使用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
Run Code Online (Sandbox Code Playgroud)


l0b*_*0b0 6

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

$ cat subshell.sh 
#!/usr/bin/env bash
declare | grep -e '^PATH=' -e '^foo='
Run Code Online (Sandbox Code Playgroud)

相比

$ 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
Run Code Online (Sandbox Code Playgroud)

$ 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
Run Code Online (Sandbox Code Playgroud)

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