shell、子shell、变量和export

[转] http://roclinux.cn/?p=1277

情景一:建立一个shell脚本var1.sh,其内容为:

[rocrocket@rocrocket SHELL]$ cat -n var1.sh
1    #!/bin/bash
2    echo :$myvar:

然后在终端上执行变量赋值语句并用echo显示此变量:

[rocrocket@rocrocket SHELL]$ myvar=hello
[rocrocket@rocrocket SHELL]$ echo $myvar
hello

但是当执行var1.sh脚本,想显示myvar变量时,情况却并非你所愿:

[rocrocket@rocrocket SHELL]$ bash ./var1.sh
::

显然,var1.sh并没有看到用户在终端中定义的myvar变量。可见,在终端中设置的变量,在运行的shell脚本中是无法看到的。

情景二:我们建立一个脚本var2.sh,内容为:

[rocrocket@rocrocket SHELL]$ cat -n var2.sh
     1  #!/bin/bash
     2  myvar=50
     3  echo :$myvar:

此时,用户在终端设置myvar的值并显示此值,然后再运行var2.sh,看看结果吧:

[rocrocket@rocrocket SHELL]$ myvar=100
[rocrocket@rocrocket SHELL]$ echo $myvar
100
[rocrocket@rocrocket SHELL]$ bash var2.sh
:50:

可见,终端中设置的值,在shell脚本中是无法改变的;而且,终端也无法改变shell脚本里面设置的变量的值。

阶段总结:

Shell工作机制中,存在一个子shell的概念,上面的两个情景展现的就是子shell的形象。子shell是登录shell为了运行某个脚本程序而建立的一个全新的shell,这个全新的shell只会使用自己的局部变量,对父shell(登录shell)的局部变量不屑一顾。而且,子shell和父shell对对方的局部变量都不会有任何冒犯,井水不犯河水。

引子:

有些事情偏偏是需要子shell和父shell有所交互的,这就需要有一个变量是子shell和父shell都能看到和修改的。这时候,export出现了,它可以导出变量

情景三:建立一个新的shell脚本var3.sh,其内容如下:

[rocrocket@rocrocket SHELL]$ cat -n var3.sh
     1  #!/bin/bash
     2  echo mylocal=$mylocal
     3  echo myglobal=$myglobal

var3.sh脚本中,设置了两个变量,一个是mylocal,表示局部变量,一个是myglobal,表示全局变量。
在终端上给此两个变量赋值,然后执行脚本程序var3.sh,很明显,结果如你所愿,是空值:

[rocrocket@rocrocket SHELL]$ mylocal=1
[rocrocket@rocrocket SHELL]$ myglobal=2
[rocrocket@rocrocket SHELL]$ bash var3.sh
mylocal=
myglobal=

我们针对myglobal执行export来导出变量,再看看结果:

[rocrocket@rocrocket SHELL]$ export myglobal
[rocrocket@rocrocket SHELL]$ bash var3.sh
mylocal=
myglobal=2

看,我们的脚本程序已经看到了myglobal的值了,看来exportmyglobal导出,真是起到作用了。

阶段总结:

他的内幕是这样的:当使用export来导出一个变量时,当前shell就会将此变量放到导出变量列表中,一旦在某个时刻需要建立子shell时,就会将这个导出变量列表拷贝一份给子shell,这样子shell也就有所应当的看到了这些被导出的变量了。

(为了能让10岁以前小朋友看懂,更形象的说:从前有个母亲叫shellshell她手里攥着导出变量列表局部变量列表两件宝贝,当shell生育了小孩(名叫子shell)的时候,她就把导出变量列表这件宝贝复制一份,交给她的孩子,而shell手里的另一件宝贝局部变量列表则不会交给小孩)

引子:

现在子shell如果想改变这份导出变量列表中的某些内容,那么父shell到底能不能察觉呢?父shell会不会随着子shell一起修改这些值呢?

情景四:看看整个过程:

[rocrocket@rocrocket SHELL]$ export myglobal=10
[rocrocket@rocrocket SHELL]$ cat -n var4.sh
     1  #!/bin/bash
     2  myglobal=33
     3  echo myglobal=$myglobal
[rocrocket@rocrocket SHELL]$ bash var4.sh
myglobal=33
[rocrocket@rocrocket SHELL]$ echo $myglobal
10

可见,在子shell是无法改变父shell中的导出变量列表中变量的值的。子shell只能修改自己手里的那份导出变量列表中变量的值。

阶段总结:

shell是无法改变父shell中的导出变量列表中变量的值的。
变量一旦被导出,对所有后续执行的子shell,该变量都是导出变量。

关于export -p

可以执行export -p来查看当前shell的导出变量列表。这其中一定是包括了从父shell继承来的导出变量列表。

全文总结:

1 没有导出的变量是局部变量,子shell是看不到的。
2
导出变量列表被复制给子shell,子shell可以修改和存取它,但是这种修改父shell看不到。
3
导出变量列表的上述特性对于直接产生的子shell生效,对于由子shell产生的后继子shell也是如此。
4 export
可以在变量赋值之后用,也可以在变量赋值之前用。

【转】http://doc.linuxpk.com/3862.html

 我自己也是一个菜鸟,接触linux没有多久,最近在学习BASH的export命令时碰到了一个难道(书上说export是将自定义变量变成系统环境变量):我在一个脚本文件中定义一个了变量,然后export变量,按照我自己的想法,执行完这个脚本后,在提示符下一定可以用echo显示出它的值,可结果却不是这样,脚本执行完后用set根本看不到有这个变量存在。为什么呢?我百思不得其解,最后将问题贴出来,一位前辈告诉我说用source+脚本文件就可以了,我试了一下果然可以,但一个新的问题又出来了。我将脚本中export命令删除后,用source一样可以。那这个export好像没有什么用呀。

  在经过多次尝试后发现了一些东西,是我自己猜的,如果有什么不对的地方,请指正,谢谢。

  执行一个脚本时,会先开启一个子shell环境(不知道执行其它程序是不是这样),然后将父shell中的所有系统环境变量复制过来,这个脚本中的语句就在子shell中执行。(也就是说父shell的环境变量在子shell中可以调用,但反过来就不行,如果在子shell中定义了环境变量,只对该shell或者它的子shell有效,当该子shell结束时,也可以理解为脚本执行完时,变量消失。)为了证明这一点,请看脚本内容:

  test='value'

  export test

  这样的脚本执行完后,test实际上是不存在的。接着看下面的:

  test='value'

  export test

  bash

  这里在脚本最后一行再开一个子shell,该shell应该是脚本文件所在shell的子shell,这个脚本执行完后,是可以看到test这个变量的,因为现在是处于它的子shell中,当用exit退出子shell后,test变量消失。

  如果用source对脚本进行执行时,如果不加export,就不会在子shell中看到这个变量,因为它还不是一个系统环境变量呀,如脚本内容是:

  test='value'

  用source执行后,在shell下是能看到这个变量,但再执行bash开一个子shell时,test是不会被复制到子shell中的,因为执行脚本文件其实也是在一个子shell中运行,所以我再建另一个脚本文件执行时,是不会输入任何东西的,内容如:echo $test。所以这点特别注意了,明明在提示符下可以用echo $test输出变量值,为什么把它放进脚本文件就不行了呢?

  所以得出的结论是:1、执行脚本时是在一个子shell环境运行的,脚本执行完后该子shell自动退出;2、一个shell中的系统环境变量才会被复制到子shell中(用export定义的变量);3、一个shell中的系统环境变量只对该shell或者它的子shell有效,该shell结束时变量消失(并不能返回到父shell中)。3、不用export定义的变量只对该shell有效,对子shell也是无效的。

  后来根据版主的提示,整理了一下贴子:为什么一个脚本直接执行和用source执行不一行呢?这也是我自己碰到的一个问题。manual原文是这样的:Read and execute commands from filename in the current shell environment and return the exit status of the last command executed from filename.明白了为什么不一样了吧?直接执行一个脚本文件是在一个子shell中运行的,而source则是在当前shell环境中运行的。根据前面的内容,你也已经明白其中的道理了吧。

  困扰了我几天的问题终于可以圆满的解决了。

 

猜你喜欢

转载自caoruntao.iteye.com/blog/1018656