我正在读这本书“Linux 命令行和 Shell 脚本圣经“第三版。第 279 页,我在此引用:
“命令替换会创建所谓的
subshell
来运行封闭的命令。子 shell 是从运行脚本的 shell 生成的单独子 shell。因此,您在脚本中创建的任何变量都不可用于子 shell 命令。如果您使用路径从命令提示符运行命令,也会创建子 shell
./
,但如果您只是运行没有路径的命令,则不会创建子 shell。”
最后一句话让我很困惑。我用一个导出一些变量的简单脚本进行了测试;脚本存在后,变量不会持续存在,无论脚本是从./
路径调用的,还是将脚本放入路径/usr/bin
并在没有路径的情况下运行的。在我看来,它如何调用子shell并没有什么区别。
我错过了什么?
答案1
“命令替换会创建所谓的子 shell 来运行所包含的命令。子 shell 是从运行脚本的 shell 生成的单独子 shell。
这是谈论如下结构:
echo "$(date) `uname -r`"
在此示例中,我使用两种不同的子 shell 受支持符号调用两个子 shell。第一个子 shell 运行命令date
,第二个子 shell 运行uname
命令。echo
两个子 shell 终止后,原始 shell 执行该命令。
因此,您在脚本中创建的任何变量都不适用于子shell命令。
这里作者搞反了。在调用子 shell 之前创建的变量对子 shell 是可用的。在子 shell 内部创建的变量只对子 shell 可用,子 shell 终止后变量将消失。
如果您使用 ./ 路径从命令提示符运行命令,也会创建子 shell,但如果您只是运行没有路径的命令,则不会创建子 shell。”
这里作者想表达的意思不太清楚。如果你调用一个外部命令,无论是否使用路径,都会发生以下情况:
- shell
fork
s 创建一个新进程。 - 新创建的子进程执行您可能指定的任何 I/O 重定向。
- 子进程执行外部命令,此时进程不再是 shell,而是成为外部程序。(当然可以是 shell 脚本)。
它是一个新进程,因此外部命令设置的任何变量对于父进程来说都是不可用的,就像它是一个子shell一样。
作者可能指的是以下三种情况之一:
- 外部命令是格式不正确的脚本,在这种情况下,调用 shell 可能会通过猜测使用哪个 shell 来解释脚本来解决不正确的格式。(这种情况非常难以预测,因此我强烈建议不要依赖它。找到解释器的正确方法是
#!
在脚本开头有一行。) - 您可能正在调用内部命令,例如
read
,从语法上看,它类似于通过搜索找到的外部命令PATH
。但即使从语法上看,它的行为也会有所不同。由于read
将设置一个供后续命令使用的变量,因此read
它本身必须在当前进程中发生,而无需任何fork
调用。(出于性能原因,即使是可以安全地作为子进程调用的内部命令,也最好在当前进程中完成。调用fork
有点昂贵。) - 您可能使用
. file
或来获取 shell 命令source file
。在这种情况下file
不是脚本,但有点类似。 中的所有命令file
都将由当前 shell 调用,而无需先调用fork
。(但当然,里面的命令file
可以触发fork
调用)。在这种情况下 中的任何#!
行都file
将被忽略,并且 由 设置的任何变量file
都将可供当前 shell 使用。