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


72

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

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

如果是这样,我们为什么需要export?我想念什么?

默认情况下,shell变量是否不属于环境?有什么区别?

Answers:


74

您的假设是shell变量在环境中。这是不正确的。该export命令定义了一个完全在环境中的名称。从而:

a=1 b=2
export b

会导致当前外壳知道$a扩展为1和$b2,但是子进程将不知道任何事情,a因为它不是环境的一部分(即使在当前外壳中也是如此)。

一些有用的工具:

  • set:对于查看当前shell的参数很有用,可以导出或不导出
  • set -k:在环境中设置分配的参数。考虑f() { set -k; env; }; f a=1
  • set -a:告诉shell将设置的任何名称放入环境中。就像export在每次作业前都放。对.env文件有用,如中所示set -a; . .env; set +a
  • export:告诉外壳程序在环境中放置一个名称。导出和分配是两个完全不同的操作。
  • env:作为外部命令,env只能告诉您有关继承的环境的信息,因此,对于进行健全性检查很有用。
  • env -i:对于启动子流程之前清除环境很有用。

替代export

  1. name=val command #在命令导出之前将该命令导出到命令中。
  2. declare/local -x name #导出名称,当您要避免将名称暴露在外部作用域时,此名称在shell函数中特别有用。
  3. set -a #导出以下所有分配。

3
set -k是可以cmd ENVVAR=value代替的ENVVAR=value cmd,除非set -k在调用之前运行,否则在您的示例中无效f。而且,如今,只有很少的Shell支持它,并且仅是为了与Bourne Shell向后兼容。在Bourne(或Korn)shell中,这不适用于函数。并且由于它影响shell解析,因此必须在shell 读取在那里使用它的代码时生效。
斯特凡Chazelas

1
您可能还需要提set -a
斯特凡Chazelas

24

Shell变量和环境变量之间有区别。如果定义外壳变量而不使用export它,则该变量不会添加到流程环境中,因此也不会继承到其子级。

使用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

6

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

$ 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不是由外壳程序导出的,而且test2.sh从不导出它,因此它subshell.sh在上次运行时也不属于环境的一部分。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.