bash 变量何时导出到脚本可访问的子 shell and/or?

When are bash variables exported to subshells and/or accessible by scripts?

我对 bash 变量是否导出到 subshells 以及脚本何时可以访问它们感到困惑。到目前为止,我的经验使我相信 bash 变量自动可用于子 shell。例如:

> FOO=bar
> echo $FOO
bar
> (echo $FOO)
bar

以上似乎表明 bash 变量在 subshell 中是可访问的。

鉴于此脚本:

#! /usr/bin/bash
# c.sh

func()
{
  echo before
  echo ${FOO}
  echo after
}

func

我知道在当前 shell 上下文中调用脚本可以访问当前 shell 的变量:

> . ./c.sh 
before
bar
after

如果我在没有 "dot space" 先例的情况下调用脚本...

> ./c.sh 
before

after

...不就是在subshell中调用了脚本吗?如果是这样,而且当前 shell 的变量可用于 subshells 也是正确的(正如我从第一个代码块推断的那样),为什么 $FOO 不可用于c.sh 什么时候 运行 这样?

同样,当 c.sh 是 运行 在括号内时,为什么 $FOO 也不可用 - 我的理解是 运行 在子 [=44] 中表达=]:

> (./c.sh)
before

after

(如果这不会使 post 问题太多:如果“./c.sh”和“(./c.sh)”都 运行 子脚本当前shell的shell,这两种调用方式有什么区别?)

(...) 在单独的环境中运行 ...,这是最容易实现的(并在 bash、破折号和大多数其他 POSIX-y shell 中实现s) 使用 subshell——也就是说,通过 fork()ing 旧的 shell、 创建的 child 但不调用任何 execv-族函数。因此,parent 的整个 in-memory 状态被复制,包括 non-exported shell 变量。对于子 shell,这正是您通常想要的:只是 parent shell 进程映像的副本,而不是用新的可执行映像替换,从而保留其所有状态到位。

(. shell-library.bash; function-from-that-library "$preexisting_non_exported_variable") 为例:由于 parens 它 fork() 是一个子 shell,但它随后获取 shell-library.bash 直接在 shell 中,而不用单独的可执行文件替换由 fork() 创建的 shell 解释器。这意味着 function-from-that-library 可以从 parent shell 中看到 non-exported 函数和变量(如果它是 execve() 则不能),并且是启动速度更快(因为它不需要 link、加载和初始化新的 shell 解释器,就像在 execve() 操作期间发生的那样);但它对 in-memory 状态、shell 配置和进程属性(如工作目录)所做的更改也不会修改调用它的 parent 解释器(如果有没有 subshell 并且它不是 fork()'d),因此 parent shell 受到保护,不会被库进行可能修改其后续操作的配置更改。


相比之下,

./other-scriptother-script 作为完全独立的可执行文件运行;它 not 在 child shell 之后保留 non-exported 变量(这不是子 shell!) 已被调用。其工作方式如下:

  • shell 调用 fork() 创建一个 child。此时,child 仍然复制了 non-exported 个变量状态。
  • child 接受任何重定向(如果是 ./other-script >>log.out,child 会 open("log.out", O_APPEND) 然后 fdup() 描述符到 1, 覆盖标准输出).
  • child调用execv("./other-script", {"./other-script", NULL})指示操作系统用other-script的新实例替换它。本次调用成功后,child的PID下的进程运行是一个全新的程序,只有exported变量存活。