当我们在一个 shell 里运行一个脚本程序时,该 shell 就会 fork 出一个新进程,从而启动了另一个命令解释器(由脚本中第一行的 #!/bin/xxx 指定,如 bash shell)来解释运行我们这个脚本。也就是说,这个新进程是一个子 shell,而之前的 shell 是个父 shell 。
在我们所运行的脚本里,我们还可以启动新的子 shell 进程,这些子 shell 进程使脚本并行地运行着多个子任务。一般而言,在一个脚本里执行一个外部命令(普通的可执行文件)时,shell 会 fork 出一个子进程,然后再用 exec 来执行这个程序;但是,bash shell 的内置命令(builtin)却不会这样,它们是直接执行的。所以,等价的内置命令的执行速度会比执行外部命令要来的快。
在一对括号 (...) 里可以放置一组指令,这些指令是在一个子 shell 里执行的。在子 shell 里的变量不能被这段子 shell 外的代码直接访问,也就是说子 shell 里的变量不能被父 shell 所存取,实际上它们是 局部变量 。
这里可以参考: (( ))和 [[ ]] 和 shell 与 命令的执行 这两篇文章。
下面用一段代码进行测试:
运行输出: 在上面的代码中, BASH_SUBSHELL 是一个环境变量,它表示进入子 shell 的层级,比如处于当前 shell 时,该变量值为 0;当在当前 shell 派生的子 shell 里时,该变量值为 1;如果该子 shell 又派生出一个子 shell,那么该变量在此间的值就为 3,以此类推。
在代码中,( ) 里的代码段是在子 shell 里执行的,而 inner_variable 作为局部变量,它的值可以在 ( ) 这段代码里 echo 出来,但是一旦返回到父shell 时,它就是未定义的,所以会输出“ inner_variable undefined in main body of shell”。也就是说,局部变量不能被外部代码所访问。
从输出可以看到,在子 shell 中和父 shell 中变量 outer_variable 的输出值是一样的;相对应的 outer_variable2 变量即使在子 shell 中进行了修改,但是当返回到父 shell 对其输出时,它却还是父 shell 中原来所赋的值。从这里可以看出,子 shell 可以 “感知” 父 shell 中的变量,但它不能修改它。其本质的原因和 fork() 函数的原理有关。在 UNIX/LINUX 中,fork 出来的子进程实际上是对父进程的一种拷贝,而子 shell 就是父shell fork 出来的一个子进程,所以它理所当然的有了父shell 中的一片拷贝。所以,子 shell 里的 outer_variable 和 outer_variable2 变量虽然和父 shell 的同名,但它们并不是同一个变量,而是父 shell 里的一个副本。
说到父shell 和 子 shell,那么会想到 export 这个命令。export 也是 bash 的一个内置命令。它主要是用来将父 shell 里的变量导出供子 shell 使用。它有如下特征:
1. 用 export 导出的变量放在“导出变量列表”中,它可以被子 shell (子 shell 的子 shell 也是如此)拷贝并使用。
2. 被 export 出来的变量虽然可以被子 shell 使用,但它也只是一个拷贝,而不会影响到父 shell 中的值以及其它子 shell 中的值。
看下面示例;
1. 先在当前 shell 里 export 一个变量:
2. 运行一个脚本 echo 此变量(该脚本只有一句话即 echo $exp8temp ): 由上可见,父 shell 里 export 的变量可以被子 shell 读取。
3. 测试一下子 shell 更改此变量是否会影响父 shell 里的值,子 shell 代码如下:
检验上面的情景: 可见子 shell 对父 shell 里 export 出来的变量进行修改并不能影响到父 shell。这说明了,子 shell 只是在“导出变量列表“里对该变量进行了一个拷贝。但反过来,父shell再次更改此变量时,子 shell 再去读时,读到的是新值,而不是原来的值。
4. 如果在子 shell 里 export 出的变量,父 shell 是否能读到呢?
先将下面一段代码放在后台运行:
然后在在 30 秒内在父 shell 里读取一下 $exp9temp 的值,发现输出为空。所以我们得出结论,export 出来的变量不能导出到父进程或者是父进程的环境里。一个自己称可以继承父进程的东西,而不能反过来去影响父进程。
那么子 shell 有什么办法可以向父 shell 传递自己的变量吗?下面方法可以考虑:
1. 通过一个中间文件进行 :
运行输出:
2. 通过命令替换 : 运行输出: 执行命令替换符(两个反单引号)之间的命令也是在子 shell 来完成的。
3. 使用命名管道 :
运行输出: 关于有名管道创建命令 mkfifo 可参考: http://www.groad.net/bbs/read.php?tid-3707.html
4. 使用 here 文档 :
运行输出:
在我们所运行的脚本里,我们还可以启动新的子 shell 进程,这些子 shell 进程使脚本并行地运行着多个子任务。一般而言,在一个脚本里执行一个外部命令(普通的可执行文件)时,shell 会 fork 出一个子进程,然后再用 exec 来执行这个程序;但是,bash shell 的内置命令(builtin)却不会这样,它们是直接执行的。所以,等价的内置命令的执行速度会比执行外部命令要来的快。
在一对括号 (...) 里可以放置一组指令,这些指令是在一个子 shell 里执行的。在子 shell 里的变量不能被这段子 shell 外的代码直接访问,也就是说子 shell 里的变量不能被父 shell 所存取,实际上它们是 局部变量 。
这里可以参考: (( ))和 [[ ]] 和 shell 与 命令的执行 这两篇文章。
下面用一段代码进行测试:
[Bash shell]
syntaxhighlighter_viewsource
syntaxhighlighter_copycode
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
#!/bin/bash
echo
"Subshell level = $BASH_SUBSHELL"
outer_variable=Outer
outer_variable2=Outer2
(
echo
"Subshell level INSIDE subshell = $BASH_SUBSHELL"
inner_variable=Inner
outer_variable2=Outer_var_changein_subshell
echo
"From Subshell,\"inner_variable\"=$inner_variable"
echo
"From parent shell,\"outer\"=$outer_variable"
echo
"From parent shell, \"outer\"=$outer_variable2"
)
echo
"In parent shell, check \"outer_variable\" value:$outer_variable"
echo
"In parent shell, check \"outer_variable2\" value:$outer_variable2"
echo
echo
"Subshell level OUTSIDE subshell = $BASH_SUBSHELL"
echo
if
[ -z
"$inner_variable"
]
then
echo
"inner_variable undefined in main body of shell"
else
echo
"From main body of shell,\"inner_variable\"=$inner_variable"
fi
exit
0
|
运行输出: 在上面的代码中, BASH_SUBSHELL 是一个环境变量,它表示进入子 shell 的层级,比如处于当前 shell 时,该变量值为 0;当在当前 shell 派生的子 shell 里时,该变量值为 1;如果该子 shell 又派生出一个子 shell,那么该变量在此间的值就为 3,以此类推。
在代码中,( ) 里的代码段是在子 shell 里执行的,而 inner_variable 作为局部变量,它的值可以在 ( ) 这段代码里 echo 出来,但是一旦返回到父shell 时,它就是未定义的,所以会输出“ inner_variable undefined in main body of shell”。也就是说,局部变量不能被外部代码所访问。
从输出可以看到,在子 shell 中和父 shell 中变量 outer_variable 的输出值是一样的;相对应的 outer_variable2 变量即使在子 shell 中进行了修改,但是当返回到父 shell 对其输出时,它却还是父 shell 中原来所赋的值。从这里可以看出,子 shell 可以 “感知” 父 shell 中的变量,但它不能修改它。其本质的原因和 fork() 函数的原理有关。在 UNIX/LINUX 中,fork 出来的子进程实际上是对父进程的一种拷贝,而子 shell 就是父shell fork 出来的一个子进程,所以它理所当然的有了父shell 中的一片拷贝。所以,子 shell 里的 outer_variable 和 outer_variable2 变量虽然和父 shell 的同名,但它们并不是同一个变量,而是父 shell 里的一个副本。
说到父shell 和 子 shell,那么会想到 export 这个命令。export 也是 bash 的一个内置命令。它主要是用来将父 shell 里的变量导出供子 shell 使用。它有如下特征:
1. 用 export 导出的变量放在“导出变量列表”中,它可以被子 shell (子 shell 的子 shell 也是如此)拷贝并使用。
2. 被 export 出来的变量虽然可以被子 shell 使用,但它也只是一个拷贝,而不会影响到父 shell 中的值以及其它子 shell 中的值。
看下面示例;
1. 先在当前 shell 里 export 一个变量:
2. 运行一个脚本 echo 此变量(该脚本只有一句话即 echo $exp8temp ): 由上可见,父 shell 里 export 的变量可以被子 shell 读取。
3. 测试一下子 shell 更改此变量是否会影响父 shell 里的值,子 shell 代码如下:
[Bash shell]
syntaxhighlighter_viewsource
syntaxhighlighter_copycode
1
2
3
4
5
|
#!/bin/bash
exp8temp=
"hello shell"
echo
$exp8temp
|
检验上面的情景: 可见子 shell 对父 shell 里 export 出来的变量进行修改并不能影响到父 shell。这说明了,子 shell 只是在“导出变量列表“里对该变量进行了一个拷贝。但反过来,父shell再次更改此变量时,子 shell 再去读时,读到的是新值,而不是原来的值。
4. 如果在子 shell 里 export 出的变量,父 shell 是否能读到呢?
先将下面一段代码放在后台运行:
[Bash shell]
syntaxhighlighter_viewsource
syntaxhighlighter_copycode
1
2
3
4
5
6
7
|
#!/bin/bash
export
exp9temp=
"hello world"
sleep
30
exit
0
|
然后在在 30 秒内在父 shell 里读取一下 $exp9temp 的值,发现输出为空。所以我们得出结论,export 出来的变量不能导出到父进程或者是父进程的环境里。一个自己称可以继承父进程的东西,而不能反过来去影响父进程。
那么子 shell 有什么办法可以向父 shell 传递自己的变量吗?下面方法可以考虑:
1. 通过一个中间文件进行 :
[Bash shell]
syntaxhighlighter_viewsource
syntaxhighlighter_copycode
01
02
03
04
05
06
07
08
09
10
|
#!/bin/bash
(
subvar=
"hello shell"
echo
"$subvar"
> temp.txt
)
read
pvar < temp.txt
echo
$pvar
|
运行输出:
2. 通过命令替换 : 运行输出: 执行命令替换符(两个反单引号)之间的命令也是在子 shell 来完成的。
3. 使用命名管道 :
[Bash shell]
syntaxhighlighter_viewsource
syntaxhighlighter_copycode
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
#!/bin/bash
mkfifo
-m 777 npipe
(
subsend=
"hello world"
echo
"$subsend"
> npipe &
)
read
pread < npipe
echo
"$pread"
exit
0
|
运行输出: 关于有名管道创建命令 mkfifo 可参考: http://www.groad.net/bbs/read.php?tid-3707.html
4. 使用 here 文档 :
[Bash shell]
syntaxhighlighter_viewsource
syntaxhighlighter_copycode
1
2
3
4
5
6
7
8
|
#!/bin/bash
read
pvar << HERE
`subvar=
"hello shell"
echo
$subvar`
HERE
echo
$pvar
|
运行输出:
方法应该还有很多,这些方法的本质原理基于进程间的通信。
转载地址:http://www.groad.net/bbs/thread-3708-1-1.html