什么时候bash变量导出到子shell和/或脚本可以访问?

Sto*_*row 11 bash shell subprocess subshell

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

> FOO=bar
> echo $FOO
bar
> (echo $FOO)
bar
Run Code Online (Sandbox Code Playgroud)

以上似乎表明bash变量可以在子壳中访问.

鉴于此脚本:

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

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

func
Run Code Online (Sandbox Code Playgroud)

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

> . ./c.sh 
before
bar
after
Run Code Online (Sandbox Code Playgroud)

如果我在没有"点空间"先例的情况下调用脚本...

> ./c.sh 
before

after
Run Code Online (Sandbox Code Playgroud)

...是不是在子shell中调用脚本的情况?如果是这样,并且当前shell的变量可用于子shell(正如我从最初的代码块推断的那样)也是$FOO如此,为什么c.sh在以这种方式运行时不可用?

类似地,为什么在括号内运行$FOO时也不可用c.sh- 我理解为在子shell中运行表达式:

> (./c.sh)
before

after
Run Code Online (Sandbox Code Playgroud)

(如果这个问题没有太多问题:如果" ./c.sh"和" (./c.sh)"都在当前shell的子shell中运行脚本,那么两种调用方式之间有什么区别?)

Cha*_*ffy 9

(...)...在一个单独的环境中运行,最容易实现(并在bash,dash和大多数其他POSIX-y shell中实现)使用子shell - 也就是说,通过fork()旧shell 创建的子项,但不调用任何子execv shell -家庭功能.因此,父项的整个内存中状态是重复的,包括未导出的shell变量.


./other-script相反,它other-script作为一个完全独立的可执行文件运行; 它并没有在子shell后保留非出口变量(这是不是一个子shell!)已被调用.其工作原理如下:

  • shell调用fork()创建一个孩子.此时,孩子仍然有甚至未导出的变量状态被复制.
  • 孩子尊重任何重定向(如果是./other-script >>log.out,孩子会open("log.out", O_APPEND),然后fdup()描述符1覆盖,覆盖stdout).
  • 孩子打电话execv("./other-script", {"./other-script", NULL}),指示操作系统用新的实例替换它other-script.在此调用成功之后,在子PID下运行的进程是一个全新的程序,只有exported变量才能生存.

  • 此外,如果您使用`exec./ other-script`(运行exec()*而不首先执行*forking),则另一个脚本会继承导出的变量,但不会继承未导出的shell变量.`./ other-script`大多相当于`(exec ./other-script)`,其中`()`分支一个子shell(保留非导出变量),然后`exec`有效地退出当前shell(销毁非导出变量)并在同一进程中运行新shell. (3认同)
  • 这很有趣——我从来没有考虑过在 `bash` 的上下文中使用 fork() 和 exec()。我的理解是否正确:当我调用 `(./c.sh)` 时,一个 _subshel​​l_ 被分叉,因此 `$FOO` 在子外壳中可见。但是那个子shell然后是`fork()`s和`exec()`s`./c.sh`,因此在`c.sh`的上下文中(这是shell的“孙子进程”,我输入 "`(./c.sh)`"),`$FOO` 不再可见? (2认同)
  • @cdarke:那不是一种讽刺; 它由Posix定义:"[`$$`]扩展为被调用shell的十进制进程ID.在子shell中(请参阅Shell执行环境),'$'将扩展为与当前shell相同的值." (2认同)