2014-09-27 59 views
3

学习Bash的书提到,一个子shell会只继承环境variabels和文件描述符,...等等,它不会继承变量未出口的击子shell神秘

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

$ 

我所知shell会为()case和./file创建两个子shell,但是为什么在()情况下subhell会标识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应有相同的父进程地址空间,所以为什么在()情况下变量对子shell是可见的,并且对于./file大小写也不会发生这种情况,尽管相同的参数是基于克隆系统调用的。

+0

你是怎么做strace for()的? – 2016-01-22 01:27:03

回答

3

子shell创建脚本调用一样。此时,来自父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)。

这些跟踪我比较了使用diff -y -W 300均衡像地址的所有典型和常见的差异后的PID:

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个字符)才能正确查看差异输出。因此,让你的窗口宽;-)

大约140线我找到clone()调用这是或多或少fork(),所以它将当前进程拆分为两个。在那儿,孩子开始做事,就像我们在跟踪中看到的那样。在165线附近,我看到了execve()的呼叫,但只是在调用脚本的情况下,因此孩子自愿放弃了很多环境并设置了一个故意的环境。圆括号的情况不会改变它的环境(它不会调用execve()),所以子进程继续有全套设置。

2

你有你的var出口子进程:

export var=15 

导出后,该变量是在推出的时间用于所有儿童的过程(不出口时间)。

var=15 
export var 

是相同

export var 
var=15 

是相同

export var=15 

导出可使用unset被取消。样品:unset var

+2

这与这个问题有什么关系? – bobah 2014-09-27 21:52:00

+0

这个问题已经被编辑,因为我的答案... – 2014-09-27 21:54:59

+2

感谢您的回答,但我想知道情况背后真正发生了什么 – user3718463 2014-09-27 22:26:58

1

这个神秘的解决方案是,子shell继承了从包括所有shell变量的母贝,因为他们仅仅是用叉子或克隆调用,所以它们与父进程相同的内存空间,这就是为什么这将工作

$ var=15 
$ (echo $var) 
15 

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

使用括号并 使用 execve()呼吁新工艺
[pid 26387] execve("./file", ["./file"], [/* 75 vars */]) = -1 ENOEXEC (Exec format error) 
+0

你打我5分钟;-)+¹ – Alfe 2014-09-27 23:21:25

+1

哈哈哈不要介意花花公子^ _ ^,你解释它比我做得更好,并使之更加清晰 – user3718463 2014-09-27 23:30:15