【发布时间】:2024-01-20 05:48:01
【问题描述】:
我对@987654321@ 变量是否导出到子shell 以及它们何时可以被脚本访问感到困惑。到目前为止,我的经验使我相信 bash 变量自动可用于子 shell。例如:
> FOO=bar
> echo $FOO
bar
> (echo $FOO)
bar
以上似乎表明bash 变量可以在子shell 中访问。
鉴于此脚本:
#! /usr/bin/bash
# c.sh
func()
{
echo before
echo ${FOO}
echo after
}
func
我知道在当前 shell 上下文中调用脚本可以访问当前 shell 的变量:
> . ./c.sh
before
bar
after
如果我在没有“点空间”先例的情况下调用脚本...
> ./c.sh
before
after
...不是在子shell中调用脚本的情况吗?如果是这样,并且当前 shell 的变量也可用于子 shell(正如我从第一个代码块中推断的那样),为什么 $FOO 在以这种方式运行时对 c.sh 不可用?
同样,当c.sh 在括号内运行时,为什么$FOO 也不可用 - 我理解这意味着在子shell 中运行表达式:
> (./c.sh)
before
after
(如果这不会让这篇文章有太多问题:如果“./c.sh”和“(./c.sh)”都在当前shell的子shell中运行脚本,那么这两种调用方式有什么区别?)
【问题讨论】:
-
一个子shell是从父进程中派生出来的,所以一个变量不需要导出在其中可见:子进程总是继承其父进程的100%状态(除了 PID 本身,以及使用指示操作系统不要在 fork 上复制它们的标志显式打开的文件描述符)。
-
所以
./foo不在子shell 中运行foo:这是一个完全不相关的子进程,不仅在fork()后面,而且在execve()边界后面。 -
...而
(./c.sh)分叉出一个子shell,然后从它内部运行一个子进程,因此子进程是原始shell 的孙子进程而不是直接子进程,并且您有孩子和孙子之间的execv边界(尽管父母和孩子之间没有)。 -
您标记了
shell,所以我想指出并非所有shell 都以与bash相同的方式处理子shell。例如,Korn shell 避免为子 shell 创建子进程。 -
@cdarke, ...我宁愿说 ksh 实现了
(...)的“独立环境”语义而不使用子shell 尽可能(当它变得不可能遵守 POSIX 语义时创建一个子shell,就会创建一个子shell;暗示(...)根本不使用它们是不准确的)。阅读以上内容作为编辑我的答案以不再声明(...)请求子shell(相对于请求最容易用子shell 实现的独立环境)的请求是公平的。
标签: bash shell subprocess subshell