Bash subshel​​l之谜

时间:2014-09-27 21:41:15

标签: linux bash shell

Learning Bash Book提到子shell只会继承环境变量和文件描述符,等等,并且它不会继承未导出的变量

$ var=15
$ (echo $var)
15
$ ./file # this file include the same command echo $var

$

我知道shell会为()case和./file创建两个子shell,但为什么在()情况下,子shell识别var变量虽然它没有导出但是在./file情况下它没有识别它?

# Strace for () 
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25617
# Strace for ./file
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25631

我尝试使用strace来弄清楚这是如何发生的,并且我发现bash将使用相同的参数进行克隆系统调用,这意味着()和./file中的分叉进程应该具有相同的进程在父类的地址空间中,为什么在()情况下varible对子shell是可见的,而./file情况也是如此,尽管相同的参数是基于克隆系统调用的?

3 个答案:

答案 0 :(得分:3)

使用括号创建的子shell确实使用execve()调用新进程,调用脚本。此时,来自父shell的变量的处理方式不同:execve()传递一组故意的变量(脚本调用的情况)而不调用execve()(括号的情况)留下完整的一组变量完好无损。

使用strace进行的探测应该已经准确地显示了这种差异;如果你没有看到它,我只能假设你犯了几个可能的错误之一。我将删除我所做的以显示差异,然后您可以自己决定错误的位置。

我创造了两条痕迹。第一个是使用

完成的
strace -f -o bash-mystery-1.strace bash -c 'v=15; (echo $v)'

,第二个是使用

完成的
strace -f -o bash-mystery-2.strace bash -c 'v=15; ./x.sh'

x.sh是可执行脚本。)

必须使用选项-f来跟踪父shell的子级(命令行中的bash)。

在对所有典型和频繁的差异(如地址和PID)进行均衡后,我使用diff -y -W 300比较了这些跟踪:

q() {
  sed -e 's/0x[0-9a-f]*/ADDR/g' \
      -e 's/12923\|12927/PARENT/g' \
      -e 's/12924\|12928/CHILD/g'
}
diff -y -W 300 <(q < bash-mystery-1.strace) <(q < bash-mystery-2.strace) | less -S

12923和12927是我的父PID,12924和12928是我的子PID(我通过扫描跟踪文件找到了)。你当然会有不同的数字,所以调整这些。并且您将需要一个非常宽的终端(超过200个字符)来正确查看diff输出。所以让你的窗户宽阔; - )

在第140行附近,我发现clone()调用或多或少是fork(),因此将当前进程拆分为两个。在那里,CHILD开始做事,正如我们在跟踪中的以下几行中看到的那样。在第165行,我看到execve()的调用,但只是在调用脚本的案例的痕迹中,所以孩子自愿放弃了很多环境并设置了一个故意的环境。括号案例不会改变其环境(它不会调用execve()),因此子进程继续具有完整集。

答案 1 :(得分:2)

您必须导出var子进程:

export var=15

导出后,该变量将用于发布时的所有子进程(不是导出时间)。

var=15
export var

相同
export var
var=15

相同
export var=15

可以使用unset取消导出。示例:unset var

答案 2 :(得分:1)

这个谜团的解决方案是子shell继承了包含所有shell变量的父shell中的所有内容,因为它们只是使用fork或clone调用,因此它们与父shell共享相同的内存空间,这就是为什么这将起作用

$ var=15
$ (echo $var)
15

但是在./file中,子shell后面会跟着exec或execv系统调用,它会清除所有以前的父变量,但是我们仍然有环境变量 你可以使用strace使用-f来监视子子shell,你会发现有一个execv的调用

[pid 26387] execve("./file", ["./file"], [/* 75 vars */]) = -1 ENOEXEC (Exec format error)