定义带或不带导出的变量


955

export为了什么?

之间有什么区别?

export name=value

name=value

4
切记还export name=value不能移植。根据您的实际需求,尝试name=value; export name一种便携式解决方案。
三人房

Answers:


1054

export 使该变量可用于子流程。

那是,

export name=value

表示变量名称可用于您从该Shell进程运行的任何进程。如果您希望进程使用此变量,请使用export,然后从该Shell运行该进程。

name=value

表示变量作用域仅限于外壳程序,不适用于任何其他进程。您可以将其用于(例如)循环变量,临时变量等。

重要的是要注意,导出变量不会使其对父进程可用。也就是说,在生成的进程中指定和导出变量不会使其在启动它的进程中可用。


105
具体而言,导出使变量可通过环境供子进程使用。
Beano

15
我还要补充一点,如果导出文件在您“获取”的文件中(例如。filename),那么它也会将其导出到您的工作环境中。
rogerdpack

6
@rogerdpack如果不导出就不能这样做吗?cat> blah \ na = hi \ n。等等 回声$ a; 为我输出“ hi”。
David Winiecki 2013年

2
很好,即使没有出口也可以。因此,我想在采购文件时,如果使用导出,它将反映在子进程中,否则,它将仅影响本地bash环境...
rogerdpack 2013年

19
这有一个极端的情况;name=value command 确实在子流程中使变量可用command
奥利弗·查尔斯沃思

254

为了说明其他答案在说什么:

$ foo="Hello, World"
$ echo $foo
Hello, World
$ bar="Goodbye"
$ export foo
$ bash
bash-3.2$ echo $foo
Hello, World
bash-3.2$ echo $bar

bash-3.2$ 

9
另一个例子al$ foobar="Whatever" bash
Alun

70

其他人回答说,导出使变量可用于子外壳,这是正确的,但仅是副作用。导出变量时,它将把该变量放在当前shell的环境中(即shell调用putenv(3)setenv(3))。
进程的环境跨exec继承,使该变量在子shell中可见。

编辑(从5年的角度来看):这是一个愚蠢的答案。“导出”的目的是使变量“处于随后执行的命令的环境中”,无论这些命令是子shell还是子进程。天真的实现方式是将变量简单地放在shell的环境中,但这将使其无法实现export -p


6
请注意,这并非完全正确。在中bash,export确实确实将变量添加到了当前shell的环境中,但是情况并非如此dash。在我看来,将变量添加到当前shell的环境中是实现的语义的最简单方法export,但是这种行为不是强制性的。
William Pursell 2013年

7
我不确定dash与此有关。原始海报专门问了一下bash
海星

14
该问题bash带有标签,但同样适用于任何本恩-壳变体。过于具体和提供仅适用于自己的答案bash是一个大恶魔。
William Pursell 2013年

12
bash是外壳的jQuery。
2014年

2
export makes the variable available to subshells, and that is correct这是一个非常混乱的术语用法。子shell不需要export继承变量。子流程可以。
阿米特·奈杜

62

据说在生成子壳时不必导出bash,而其他人则表示完全相反。需要注意的子shell之间的区别是很重要的(那些被创建()``$()或回路)和子(即通过名称调用,如流程文字bash出现在你的脚本)。

  • 外壳程序 有权访问父级的所有变量,而不管其导出状态如何。
  • 进程看到导出的变量。

这两种构造的共同点是,它们都不会将变量传递回父外壳。

$ noexport=noexport; export export=export; (echo subshell: $noexport $export; subshell=subshell); bash -c 'echo subprocess: $noexport $export; subprocess=subprocess'; echo parent: $subshell $subprocess
subshell: noexport export
subprocess: export
parent:

还有一个混乱的根源:有人认为“分叉”子流程是那些看不到未导出变量的子流程。通常fork()之后紧跟exec(),这就是为什么fork()似乎是要寻找的东西,而实际上是exec()。您可以先运行命令,而无需先使用fork()exec,该方法启动的进程也将无法访问未导出的变量:

$ noexport=noexport; export export=export; exec bash -c 'echo execd process: $noexport $export; execd=execd'; echo parent: $execd
execd process: export

请注意,这次我们看不到该parent:行,因为我们已经用exec命令替换了父shell ,所以没有什么可以执行该命令了。


我从未见过(单独)创建子外壳程序的循环。OTOH管道可以执行此操作(总是根据最后一个而不是最后一个,有时取决于您的shell,版本和选项)。后台(&)还会创建一个子外壳。
dave_thompson_085

怎么样,这些var=asdf bash -c 'echo $var'还是var=asdf exec bash -c 'echo $var'?输出为asdf。所述;如果放置在变量定义后,使差。请问是什么解释?看起来var(no ;)以某种方式考虑生成子进程,因为原始shell与它无关。echo $var如果在第二行执行,则不打印任何内容。但是一个内衬var=asdf bash -c 'echo $var'; echo $varasdf\nasdf
4xy

31

export NAME=value 用于对子流程有意义的设置和变量。

NAME=value 用于当前shell进程专用的临时或循环变量。

更详细地,export在环境中标记变量名称,该环境将在创建后复制到子流程及其子流程。不会从子流程复制任何名称或值。

  • 一个常见的错误是在等号周围放置一个空格:

    $ export FOO = "bar"  
    bash: export: `=': not a valid identifier
    
  • B子进程仅看到导出的变量():

    $ A="Alice"; export B="Bob"; echo "echo A is \$A. B is \$B" | bash
    A is . B is Bob
    
  • 子流程中的更改不会更改主外壳:

    $ export B="Bob"; echo 'B="Banana"' | bash; echo $B
    Bob
    
  • 创建子流程时,标记为导出的变量具有复制的值:

    $ export B="Bob"; echo '(sleep 30; echo "Subprocess 1 has B=$B")' | bash &
    [1] 3306
    $ B="Banana"; echo '(sleep 30; echo "Subprocess 2 has B=$B")' | bash 
    Subprocess 1 has B=Bob
    Subprocess 2 has B=Banana
    [1]+  Done         echo '(sleep 30; echo "Subprocess 1 has B=$B")' | bash
    
  • 仅导出的变量成为环境(man environ)的一部分:

     $ ALICE="Alice"; export BOB="Bob"; env | grep "ALICE\|BOB"
     BOB=Bob
    

因此,现在应该像夏天的阳光一样清晰!感谢Brain Agnew,alexp和William Prusell。


12

export 将使该变量可用于从当前shell派生的所有shell。


11

应该注意的是,您可以导出变量,然后再更改值。变量的更改值将可用于子进程。为变量设置导出后,您必须export -n <var>删除该属性。

$ K=1
$ export K
$ K=2
$ bash -c 'echo ${K-unset}'
2
$ export -n K
$ bash -c 'echo ${K-unset}'
unset

谢谢,这正是我想要的信息,因为我看到了一个脚本,该脚本使用环境变量,然后使用新值“重新导出”它们,并想知道是否有必要。
Mike Lippert

8

您可能已经知道,UNIX允许进程具有一组环境变量,这些环境变量是键/值对,键和值都是字符串。操作系统负责为每个进程分别保留这些对。

程序可以通过以下UNIX API访问其环境变量:

  • char *getenv(const char *name);
  • int setenv(const char *name, const char *value, int override);
  • int unsetenv(const char *name);

进程还从父进程继承环境变量。操作系统负责在创建子进程时创建所有“ envar”的副本。

Bash和其他shell一样,能够根据用户要求设置其环境变量。这就是export存在的目的。

export是Bash命令,用于为Bash设置环境变量。使用此命令设置的所有变量将被此Bash创建的所有进程继承。

有关Bash环境的更多信息

Bash中的另一种变量是内部变量。由于Bash不仅是交互式外壳,因此实际上是脚本解释器,与其他解释器(例如Python)一样,它能够保留自己的变量集。应该提到的是,Bash(与Python不同)仅支持字符串变量。

定义Bash变量的符号为name=value。这些变量保留在Bash中,与操作系统保留的环境变量无关。

有关Shell参数(包括变量)的更多信息

同样值得注意的是,根据Bash参考手册:

Shell Parameters中所述,可以通过在其前面添加参数分配来临时扩展任何简单命令或功能的环境。这些赋值语句仅影响该命令看到的环境。


总结一下:

  • export用于在操作系统中设置环境变量。此后,此变量将可用于当前Bash进程创建的所有子进程。
  • Bash变量符号(名称=值)用于设置仅对当前bash进程可用的局部变量
  • 在另一个命令前面加上Bash变量表示法只会为该命令的作用域创建环境变量。

1
bash var不支持Python类型,但具有字符串,整数和两种数组(“索引” /传统和“关联”数组,类似于awk数组,perl哈希或Python dict)。其他外壳各不相同;只有字符串是可移植的
dave_thompson_085

7

接受的答案意味着这一点,但我想作出明确到shell内建的连接:

如前所述,export将使shell和子代都可以使用变量。如果export使用时,变量将只提供在壳,只有壳内建可以访问它。

那是,

tango=3
env | grep tango # prints nothing, since env is a child process
set | grep tango # prints tango=3 - "type set" shows `set` is a shell builtin

3

这是另一个例子:

VARTEST="value of VARTEST" 
#export VARTEST="value of VARTEST" 
sudo env | grep -i vartest 
sudo echo ${SUDO_USER} ${SUDO_UID}:${SUDO_GID} "${VARTEST}" 
sudo bash -c 'echo ${SUDO_USER} ${SUDO_UID}:${SUDO_GID} "${VARTEST}"'  

只有通过使用export VARTEST才能在sudo bash -c'...'中获得VARTEST的值!

有关更多示例,请参见:


3

UNIX的两个创建者Brian Kernighan和Rob Pike在他们的《 UNIX编程环境》一书中对此进行了解释。谷歌的标题,你会很容易找到一个PDF版本。

它们在3.6节中介绍了shell变量,并在该export节末尾重点介绍了命令的使用:

当您想在子Shell中访问变量的值时,应使用Shell的export命令。(您可能会想到为什么无法将变量的值从子Shell导出到其父级的原因)。


2

只是为了显示环境(env)中的导出变量与环境中的非导出变量之间的区别:

如果我这样做:

$ MYNAME=Fred
$ export OURNAME=Jim

那么只有$ OURNAME会出现在环境中。变量$ MYNAME不在环境中。

$ env | grep NAME
OURNAME=Jim

但是变量$ MYNAME确实存在于外壳中

$ echo $MYNAME
Fred

1

默认情况下,在脚本中创建的变量仅可用于当前外壳程序。子进程(子外壳程序)将无法访问已设置或修改的值。要允许子进程查看值,需要使用export命令。


0

尽管在讨论中没有明确提及,但是从bash内部生成子shell时不必使用export,因为所有变量都已复制到子进程中。


请解释一下,因为您的意思似乎与上面的示例直接矛盾。
Mike Lippert

如果您不希望全局导出变量,而仅将其提供给子流程,则这是正确的方法!谢谢。
jtblin 2014年
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.