Bash命令替换 + 参数扩展

4

我对在子shell中引用、参数和glob扩展的工作原理感到困惑。子shell命令行的引用和扩展是否始终发生在子shell进程的上下文中?我的测试似乎证实了这一点。

Tuomas@DESKTOP-LI5P50P MINGW64 ~/shell/test1/test
$ ls
a  b  c

Tuomas@DESKTOP-LI5P50P MINGW64 ~/shell/test1/test
$ echo "$(echo *)"
a b c
# The subshell expands the * glob

Tuomas@DESKTOP-LI5P50P MINGW64 ~/shell/test1/test
$ echo $(echo '*')
a b c
# The subshell outputs literal *, parent shell expands the * glob

Tuomas@DESKTOP-LI5P50P MINGW64 ~/shell/test1/test
$ echo $(echo "*")
a b c
# The subshell outputs literal *, parent shell expands the * glob

Tuomas@DESKTOP-LI5P50P MINGW64 ~/shell/test1/test
$ echo "$(echo '*')"
*
# The subshell outputs literal *, parent shell outputs literal *

Tuomas@DESKTOP-LI5P50P MINGW64 ~/shell/test1/test
$ foo=bar

Tuomas@DESKTOP-LI5P50P MINGW64 ~/shell/test1/test
$ echo "$(echo $foo)"
bar
# The subshell expands variable foo

Tuomas@DESKTOP-LI5P50P MINGW64 ~/shell/test1/test
$ echo $(echo '$foo')
$foo
# The subshell outputs literal $foo

Tuomas@DESKTOP-LI5P50P MINGW64 ~/shell/test1/test
$ echo $(echo "$foo")
bar
# The subshell expands variable foo

Tuomas@DESKTOP-LI5P50P MINGW64 ~/shell/test1/test
$ echo "$(echo '$foo')"
$foo
# The subshell outputs literal $foo

我理解您的意思是:父进程会在分叉子进程之前处理或评估子shell命令行的情况是否存在,我的理解正确吗?


你是正确的。子shell中的参数扩展在该子shell中运行。话虽如此,对于你的大多数测试所要展示的断言的真实性/错误性的哪些方面并不明显。 - Charles Duffy
更好的测试方法是将 echo "$BASHPID"echo "$(echo "$BASHPID)")" 进行比较。 - Charles Duffy
1
嗯,提到“引用”是另一回事——引用在解析时(逐个字符地!)进行评估,而扩展发生在执行时。但扩展始终在分叉后发生。 - Charles Duffy
有一件事可能有助于确保答案是有用和适用的:您正在考虑什么实际应用场景,这取决于对这个问题的回答? - Charles Duffy
1个回答

2

解析即决定引用哪些内容的方式在分叉之前进行。由于被分叉的shell拥有其父进程内存的写时复制实例,因此它也拥有解析树,并从其父进程继承该信息。

参数展开(例如您示例中的$foo)发生在分叉之后。类似地,先将foo的内容字符串分割和通配符扩展,以生成要传递给子shell中的echo的参数列表。然后该子shell运行echo,并将输出写入标准输出,由父进程(原始shell)读取。

命令替换结果(由echo写入的内容)的字符串分割和通配符扩展在父进程中进行,在它读取子进程的输出作为字符串后发生。


1
对于BASHPID而言,有其他的方法($$会是父shell的PID,除非你是子shell的子shell,如果针对Linux,你可以从procfs中获取父进程的进程ID),但总的来说,以上说法是正确的。 - Charles Duffy
1
@codeforester,这怎么可能?如果你运行 echo "$(cd /; echo "$PWD")"PWD 变量在子 shell 分叉之前是无法扩展的。子 shell 必须先尝试运行 cd 命令并确定是否成功,然后才能扩展 PWD 变量。 - Charles Duffy
这很有道理 - echo $PWD $(cd /some/other/dir; echo $PWD) 显示了不同的值。然而,为什么 echo $$ $(echo $$) 在父进程和子shell中都会回显相同的PID呢?$$ 是一个特殊情况吗? - codeforester
1
@codeforester,这是因为$$被设计成返回主shell的PID,即使在子shell中运行也是如此--这是根据文档和设计的。这就是BASHPID存在的全部原因;否则就没有必要了。(所以$$由子进程扩展,但它使用父进程在调用时初始化的值)。 - Charles Duffy
感谢您的澄清,@CharlesDuffy。这些基本操作需要很长时间才能理解。在U&L stackoverflow中找到了这篇相关文章:子shell和子进程是一回事吗 - codeforester
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接