总算不是那么的懵逼了。
基本概念
本质上是一个完整的新shell,用来跑指定的程序。
看个简单的例子:
x=100
cat test
# 输出
#!/usr/bin
echo x is $x
sh test
# 输出
x is
这里,当执行sh test
时,login shell 「即parent shell」会创建一个新的shell来跑 test 程序,这个新的shell有着自己的环境变量,并且对parent shell中的本地变量local variable一无所知,test中的变量 x 默认赋值为null,所以输出结果为x is
.
获取 parent shell 的 local variable
两种方法:
export
export 给 subshell 的变量,其实是复制了一份 parent shell 中的 local variable,所以针对 exported variable 所做的改变, 都不会影响到 parent shell 原先的同名变量。
例子:
x=10 cat var_test # 输出 #!/bin/bash echo $x if [ -n $x ]; then x=$(($x+1)) echo $x fi sh var_test # 输出,此时在var_test中 x 为 null, [ -n $x ] 为 false, 所以什么都没有输出 export x sh var_test # 输出, 此时在var_test中,对 x 进行了修改,但并不影响login shell中的 x 10 11 echo $x # 输出, 这里,x 依然是10 10
exported variables 可以一直传给后面的subshell:
cat var_test3 # 输出 y=9 z=8 echo x = $x echo y = $y echo z = $z export z sh var_test4 cat var_test4 # 输出 echo x = $x echo y = $y echo z = $z sh var_test3 # 输出 x = y = 9 z = 8 x = y = z = 8 x=100 y=90 export x export y sh var_test3 # 输出 x = 100 y = 9 z = 8 x = 100 y = 9 z = 8
这里会发现 export 给 var_test3 的变量 y,初始值为90 , 在 var_test3 中被重新赋值为 9,执行var_test4 时,export 给 var_test4 的 y 此时已经是9, 而不是最初的90。
执行脚本的时候,将需要export的变量放在脚本执行命令前面。
还是上面的例子var_test:
unset x x=99 sh var_test # 输出 99 100 echo $x # 输出, 这里 x 未赋值,默认为 null
将需要export 给subshell 的 x,放在执行命令前,上面的
x=99 sh var_test
等价于(x=10;export x;sh var_test)
, 而(….) 中所做的改变不会更改其parent shell的环境变量。
{….; } VS (…..)
{….; }: 命令在当前shell中执行,在{….; } 中做的改变会影响到当前shell.
例子:
x=10 {x=99;} echo $x # 输出 99 pwd # 输出 /Users/lucia/Linux/acme {cd ..; pwd} # 输出 /Users/lucia/Linux pwd # 输出 /Users/lucia/Linux
如果让脚本在当前shell中执行,还可以使用
.
和source
命令。. test.sh source test.sh
(….): 命令在subshell中执行,同样,在(….)做的修改,并不改变当前shell中的环境。
例子:
x=10
(x=99)
echo $x
# 输出
10
pwd
# 输出
/Users/lucia/Linux/acme
(cd ..; pwd)
# 输出
/Users/lucia/Linux
pwd
# 输出
/Users/lucia/Linux/acme
Loop 循环是 subshell
for, while, until 等 loop循环,都属于subshell。
看个例子:
cat subshell_example
# 输出
#!/bin/bash
lineno=0
cat $* |
while read line
do
lineno=$(($lineno + 1))
done
echo "$lineno"
cat sums
# 输出
9124
-750
3631
1231
-1122
# 执行 subshell_example,参数为sums
sh subshell_example sums
# 输出
0
这里,按照代码逻辑,应该输出sums文件中总行数,也就是5,但由于loop循环是一个subshell,echo "$lineno"
输出的一直是loop 外声明的lineno,也就是0。
那么如何解决?
这里可以使用 herestring解决这个问题,修改subshell_example文件:
cat subshell_example
# 输出
#!/bin/bash
lineno=0
while read line
do
lineno=$(($lineno + 1))
done <<< "$(cat $*)"
echo "$lineno"
sh subshell_example sums
# 输出
5