为什么可以在 Bash 函数中设置环境变量,而不能在脚本本身中设置

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/41810536/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 15:38:58  来源:igfitidea点击:

Why can you set environment variables in Bash functions but not in the script itself

bash

提问by Parham

Why does this work:

为什么这样做:

# a.sh
setEnv() {
    export TEST_A='Set'
}

when this doesn't:

当这不是:

# b.sh
export TEST_B='Set'

Ex:

前任:

> source a.sh
> setEnv
> env | grep TEST_A
TEST_A=Set
> b.sh
> env | grep TEST_B

I understand why running the script doesn't work and what to do to make it work (source b.shetc), but I'm curious to why the function works. This is on OS X if that matters.

我理解为什么运行脚本不起作用以及如何使它工作(source b.sh等),但我很好奇该函数为什么起作用。如果这很重要,这是在 OS X 上。

回答by Inian

You need to understand the difference between sourcingand executinga script.

您需要了解获取脚本和执行脚本之间的区别。

  • Sourcingruns the script from the parent-shell in which the script is invoked; all the environment variables are retained until the parent-shell is terminated (the terminal is closed, or the variables are reset or unset), whereas

  • Executeforks a new shell from the parent shell and those variables including your exportvariables are retained only in the sub-shell's environment and destroyed at the end of script termination.

  • Sourcing从调用脚本的父 shell 运行脚本;所有环境变量都会保留,直到父 shell 终止(终端关闭,或者变量被重置或取消设置),而

  • Execute从父 shell 派生出一个新的 shell,包括您的变量在内的那些变量export仅保留在子 shell 的环境中,并在脚本终止时销毁。

i.e. the sub-shell ( imagine it being an environment) created in the first case to hold the variables are not allocated in scope of a separate child environment but are just added in the parents' ( e.g. imagine an extra memory cell, maintained by the parent ) environment which is held until you have the session open. But executing a script is, imagine a simple analogy, calling a function whose variables are in stored in stack which loose scope at the end of function call. Likewise, the forked shell's environment looses scope at the end of its termination.

即在第一种情况下创建的用于保存变量的子外壳(想象它是一个环境)没有在单独的子环境范围内分配,而只是添加到父母的(例如想象一个额外的内存单元,由parent ) 环境,直到您打开会话为止。但是执行脚本是,想象一个简单的类比,调用一个函数,其变量存储在堆栈中,在函数调用结束时松散范围。同样,分叉的 shell 的环境在其终止结束时失去了作用域。

So it comes down to this, even if you have a functionto exportyour variable, if you don't sourceit to the current shell and just plainly executeit, the variable is not retained; i.e.

所以归结为这一点,即使你有一个函数export你的变量,如果你没有source给当前 shell 并且简单地execute说它,变量不会被保留;IE

# a.sh
setEnv() {
    export TEST_A='Set'
}

and if you run it in the shell as

如果你在 shell 中运行它

bash script.sh    # unlike/NOT source script.sh
env | grep TEST_A
                  # empty

回答by chepner

Executing a function does not, in and of itself, start a new process like b.shdoes.

执行一个函数本身并不会像b.sh这样启动一个新进程。

From the man page (emphasis on the last sentence):

从手册页(强调最后一句):

FUNCTIONS
       A shell function, defined  as  described  above  under  SHELL  GRAMMAR,
       stores  a  series  of commands for later execution.  When the name of a
       shell function is used as a simple command name, the list  of  commands
       associated with that function name is executed.  **Functions are executed
       in the context of the current shell;  no  new  process  is  created  to
       interpret  them  (contrast  this with the execution of a shell script).**

回答by mklement0

I understand why running the script doesn't work and what to do to make it work (source b.shetc)

我明白为什么运行脚本不起作用以及如何使它工作(source b.sh等)

So you already understand the fact that executing b.shdirectly -- in a child process, whose changes to the environment fundamentally won't be visible to the currentprocess (shell) -- will notdefine TEST_Bin the current(shell) process, so we can take this scenario out of the picture.

因此,您已经了解b.sh直接执行的事实——在子进程中,其对环境的更改从根本上不会对当前进程(shell)可见——不会TEST_B当前(shell)进程中定义,因此我们可以把这个场景从图片中去掉。

I'm curious why the function works.

我很好奇为什么这个功能有效。

  • When you sourcea script, you execute it in the context of the currentshell - loosely speaking, it is as if you had typed the contents of the script directly at the prompt: any changes to the environment, including shell-specific elements such as shellvariables, aliases, functions, become visible to the current shell.

  • Therefore, after executing source a.sh, function setEnvis now available in the current shell, and invoking it executes export TEST_A='Set', which defines environment variable TEST_Ain the currentshell (and subsequently created child processes would see it).

  • Perhaps your misconception is around what chepner's helpful answeraddresses: in POSIX-like shells, functionsrun in the current shell- in contrast with scripts(when run without source), for which a child processis created.

  • 当你source编写一个脚本时,你是在当前shell的上下文中执行它- 松散地说,就好像你直接在提示符下键入了脚本的内容:对环境的任何更改,包括特定于shell 的元素,例如shell变量、别名、函数对当前 shell 可见。

  • 因此,在执行之后source a.sh,函数setEnv现在在当前shell 中可用,并且调用它 executes export TEST_A='Set',它TEST_A当前shell 中定义环境变量(随后创建的子进程将看到它)。

  • 也许您的误解是围绕着chepner 的有用答案所解决的问题:在类似 POSIX 的 shell 中,函数当前 shell 中运行- 与脚本(在没有 的情况下运行时source)形成对比,后者为其创建了子进程

This is on OS X if that matters.

如果这很重要,这是在 OS X 上。

Not in this case, because only functionality built into bashitself is used.

在这种情况下不是,因为只bash使用了自身内置的功能。