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情况也是如此,尽管相同的参数是基于克隆系统调用的?
解决方法:
使用括号创建的子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行附近,我发现了一个或多或少是fork()的clone()调用,因此它将当前进程拆分为两个.在那里,CHILD开始做事,正如我们在跟踪中的以下几行中看到的那样.在165行左右,我看到了execve()的调用,但只是在调用脚本的案例的跟踪中,所以孩子自愿放弃了很多环境并设置了一个故意的环境.括号案例不会更改其环境(它不会调用execve()),因此子进程继续具有完整集.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。