什么时候 bash 变量被导出到子 shell 和/或被脚本访问? [英] When are bash variables exported to subshells and/or accessible by scripts?

查看:14
本文介绍了什么时候 bash 变量被导出到子 shell 和/或被脚本访问?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm confused over whether bash variables are exported to subshells and when they are accessible by scripts. My experience so far led me to believe that bash variables are automatically available to subshells. E.g.:

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

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

The above appears to demonstrate that bash variables are accessible in subshells.

鉴于此脚本:

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

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

func

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

I understand that calling the script in the current shell context gives it access to the current shell's variables:

> . ./c.sh 
before
bar
after

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

If I were to call the script without the "dot space" precedent...

> ./c.sh 
before

after

...不是在子shell中调用脚本的情况吗?如果是这样,并且当前 shell 的变量对子 shell 可用也是真的(正如我从第一个代码块推断的那样),为什么 $FOOc.sh 什么时候这样运行?

...isn't it the case that the script is called in a subshell? If so, and it's also true that the current shell's variables are available to subshells (as I inferred from the firstmost code-block), why is $FOO not available to c.sh when run this way?

同样,当 c.sh 在括号内运行时,为什么 $FOO 也不可用 - 我理解这意味着在子外壳中运行表达式:

Similarly, why is $FOO also unavailable when c.sh is run within parentheses - which I understood to mean running the expression in a subshell:

> (./c.sh)
before

after

(如果这不会因为太多问题而混淆这篇文章:如果./c.sh"和(./c.sh)"都在当前shell的子shell中运行脚本,这两种调用方式有什么区别?)

(If this doesn't muddy this post with too many questions: if "./c.sh" and "(./c.sh)" both run the script in a subshell of the current shell, what's the difference between the two ways of calling?)

推荐答案

(...) 在单独的环境中运行 ...,这是最容易实现的(并在 bash、dash 和大多数其他 POSIX-y 外壳中实现)使用子外壳——也就是说,通过 fork() 对旧外壳创建的子外壳,但不调用任何 execv-family 函数.因此,父级的整个内存状态被复制,包括未导出的 shell 变量.而对于子 shell,这正是您通常想要的:只是父 shell 进程映像的副本,而不是被新的可执行映像替换,从而保持其所有状态.

(...) runs ... in a separate environment, something most easily achieved (and implemented in bash, dash, and most other POSIX-y shells) using a subshell -- which is to say, a child created by fork()ing the old shell, but not calling any execv-family function. Thus, the entire in-memory state of the parent is duplicated, including non-exported shell variables. And for a subshell, this is precisely what you typically want: just a copy of the parent shell's process image, not replaced with a new executable image and thus keeping all its state in place.

(.shell-library.bash; function-from-that-library "$preexisting_non_exported_variable") 为例:由于它的括号 fork()sa 子shell,但它随后获取shell-library.bash 直接在该shell 中 的内容,而不替换由该fork()<创建的shell 解释器/code> 带有单独的可执行文件.这意味着 function-from-that-library 可以从父 shell 中看到未导出的函数和变量(如果它是 execve() 则不能),并且启动速度更快(因为它不需要像在 execve() 操作期间那样链接、加载和以其他方式初始化新的 shell 解释器);但它对内存状态、shell 配置和进程属性(如工作目录)所做的更改也不会修改调用它的父解释器(如果没有子 shell 并且它不是 fork()'d),因此可以保护父 shell 免受库所做的配置更改,这些更改可能会修改其以后的操作.

Consider (. shell-library.bash; function-from-that-library "$preexisting_non_exported_variable") as an example: Because of the parens it fork()s a subshell, but it then sources the contents of shell-library.bash directly inside that shell, without replacing the shell interpreter created by that fork() with a separate executable. This means that function-from-that-library can see non-exported functions and variables from the parent shell (which it couldn't if it were execve()'d), and is a bit faster to start up (since it doesn't need to link, load, and otherwise initialize a new shell interpreter as happens during execve() operation); but also that changes it makes to in-memory state, shell configuration, and process attributes like working directory won't modify the parent interpreter that called it (as would be the case if there were no subshell and it weren't fork()'d), so the parent shell is protected from having configuration changes made by the library that could modify its later operation.

./other-scriptother-script 作为完全独立的可执行文件运行;它确实 not 在子 shell (它不是子 shell!) 被调用后保留未导出的变量.其工作原理如下:

./other-script, by contrast, runs other-script as a completely separate executable; it does not retain non-exported variables after the child shell (which is not a subshell!) has been invoked. This works as follows:

  • shell 调用 fork() 来创建一个子进程.在这个时间点,孩子仍然复制了未导出的变量状态.
  • 孩子接受任何重定向(如果是 ./other-script >>log.out,孩子将 open("log.out", O_APPEND),然后将描述符 fdup() 转移到 1,覆盖 stdout).
  • 子调用execv("./other-script", {"./other-script", NULL})指示操作系统将其替换为other-script 的新实例.此调用成功后,子进程PID下运行的进程是一个全新的程序,只有exported变量存活.
  • The shell calls fork() to create a child. At this point in time, the child still has even non-exported variable state copied.
  • The child honors any redirections (if it was ./other-script >>log.out, the child would open("log.out", O_APPEND) and then fdup() the descriptor over to 1, overwriting stdout).
  • The child calls execv("./other-script", {"./other-script", NULL}), instructing the operating system to replace it with a new instance of other-script. After this call succeeds, the process running under the child's PID is an entirely new program, and only exported variables survive.

这篇关于什么时候 bash 变量被导出到子 shell 和/或被脚本访问?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆