gpt4 book ai didi

linux - Bash 子 shell 之谜

转载 作者:可可西里 更新时间:2023-11-01 11:46:05 30 4
gpt4 key购买 nike

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

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

$

据我所知,shell 将为 () case 和 ./file 创建两个子 shell,但为什么在 () case 中子 shell 识别了 var 变量,尽管它没有导出,而在 ./file case 中它没有识别是吗?

# 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 中的两个 fork 进程应该具有相同的进程父级的地址空间,那么为什么在 () 情况下变量对子 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 个字符)才能正确查看差异输出。所以让你的窗口变宽;-)

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

关于linux - Bash 子 shell 之谜,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26079488/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com