环境变量到底是什么?


42

我知道这VARIABLE=value会创建一个环境变量,并export VARIABLE=value使它可用于当前shell创建的进程。env显示当前的环境变量,但是它们住在哪里?什么包括环境变量(或就此而言,环境)?

Answers:


29

环境并不像看起来那样神奇。Shell将其存储在内存中并传递给execve()系统调用。子进程将其作为称为的数组指针继承environ。从execve联机帮助页:

概要

   #include <unistd.h>

   int execve(const char *filename, char *const argv[],
              char *const envp[]);

argv是传递给新程序的参数字符串数组。
按照约定,这些字符串中的第一个应包含与正在执行的文件关联的文件名。envp是一个字符串数组,通常为key = value形式,将作为环境传递给新程序。

该手册environ(7)页还提供了一些见解:

概要

   extern char **environ;

描述

该变量 environ指向一个称为“环境”的字符串指针数组。此数组中的最后一个指针的值为NULL。(必须在用户程序中声明此变量,但<unistd.h>如果头文件来自libc4或libc5,并且如果它们来自glibc并定义了_GNU_SOURCE ,则在头文件中声明。)此字符串数组可用于通过启动该过程的exec(3)调用来处理该过程。

这两个GNU手册页都符合POSIX规范


4
+1可能值得注意的是,该exec(3)家庭的某些成员(即与exec * v不匹配的那些成员)在掩体下通过了**环境。
— msw

5
请注意,这与子进程无关(子进程继承其父进程的所有内存),而是执行的程序(在同一进程中),因此这是在execve()系统调用中传递数据的另一种方式(否则会擦除内存)的过程)。
— 斯特凡Chazelas

@msw:exec*e是显式传递env的environ变量,而不是隐式使用全局变量。的v手段“载体”,并且指的是作为一个阵列(而不是“列表”(可变长度函数))通过命令行参数 execve是一个系统调用,和所有其他的exec*功能是它的libc包装。
— 彼得·科德斯

19

您有一点点错误:SOME_NAME=value创建一个shell变量(在大多数shell中)。export SOME_NAME=value创建一个环境变量。不管好坏,大多数Unix / Linux / * BSD Shell在访问环境变量和Shell变量时使用相同的语法。

从更大的意义上讲,“环境”只是程序执行所伴随的信息。在C程序中,您可能会找到带有getpid()调用的进程ID ,在Shell程序中,您将使用变量访问:$$。进程ID只是程序环境的一部分。我相信“环境”一词来自一些更理论上的计算机科学主题,例如对程序执行进行建模。程序执行模型具有一种“包含变量及其值之间的关联” 的环境。

对于后者,更严格的定义是Unix / Linux / * BSD shell的“环境”:名称(“变量”)及其值之间的关联。对于大多数Unix风格的shell,值都是字符串,尽管这并不像过去那样严格。这些天,Ksh,Zsh和Bash都输入了类型变量。甚至可以导出外壳函数定义。

使用与普通外壳变量分开的环境涉及fork/exec启动所有Unix使用的新进程的方法。当您使用export一个名称/值对时,该名称/值对将出现在新的可执行文件的环境中,由外壳程序通过execve(2)系统调用(通常在后面跟随fork(2),exec使用shell命令时除外)启动。

在之后execve(),main()新二进制文件的功能具有其命令行参数,环境(存储为指向var=value字符串的NULL终止数组,请参见environ(7)手册页)。继承的其他状态包括ulimit设置,当前工作目录以及execve()调用者未为其设置FD_CLOEXEC的任何打开的文件描述符。tty的当前状态(启用回显,原始模式等)也可以视为新exec进程继承的执行状态的一部分。

有关简单命令(内置或外壳函数除外),请参见bash手册对执行环境的描述。

Unix环境至少与某些其他操作系统不同:VMS“词汇”可以由子进程更改,并且该更改在父进程中可见。cd子进程中的VMS 将影响父进程的工作目录。至少在某些情况下,我的记忆可能使我失望。

一些环境变量是众所周知的,$HOME,$PATH,$LD_LIBRARY_PATH等。对于给定的编程系统,有些是常规的,因此父shell可以将很多特殊用途的信息传递给某个程序,例如特定的临时目录或未显示在中的用户ID和密码ps -ef。例如,简单的CGI程序会通过环境变量从Web服务器继承很多信息。


1
看起来似乎仍然有些复杂。在bash中,至少SOME_NAME=value command将为该命令调用设置SOME_NAME环境变量。令人困惑的是,似乎没有设置相同名称的shell变量。
— 塞缪尔·埃德温·沃德

2
更准确地说,环境变量不是继承的,而是显式地从Shell传递到它产生的程序。
— msw

2
@SamuelEdwinWard您的SOME_NAME=value command行为与预期相反的原因是它是一种特殊的语法,意思是“将SOME_NAME添加到传递给命令的环境中,但不要更改此Shell的变量”。
— msw

1
引人入胜的lambda微积分/函数式编程链接。这是一个很有意思的联系,很有意义。
— 马特

1
其中有些不太正确。例如,子外壳程序是子进程,必须对其进行fork()编辑,但是它们确实接收(复制)外壳程序变量。
— ruakh 2013年

7

最原始形式的环境变量只是一组名称/值对。如bash手册页(man 1 bash)中“环境”部分所述:

   When  a  program  is invoked it is given an array of strings called the
   environment.   This  is  a  list  of  name-value  pairs,  of  the  form
   name=value.

   The  shell  provides  several  ways  to manipulate the environment.  On
   invocation, the shell scans its own environment and creates a parameter
   for  each name found, automatically marking it for export to child pro-
   cesses.  Executed commands inherit the  environment.

实际上,它允许您定义与从当前Shell调用的程序共享或唯一的行为。例如,当使用crontab或时,visudo您可以定义EDITOR环境变量来定义另一个编辑器,而不是系统默认使用的编辑器。对于诸如man查看您的PAGER环境以找出应该使用哪个页面程序来显示手册页输出的命令之类的事情,也是如此。

大量的Unix命令读取环境,并根据设置的内容,根据这些内容更改其输出/处理/操作。有些是共享的,有些是该程序独有的。大多数手册页包含有关环境变量如何影响所描述程序的信息。

其他实用示例适用于诸如在同一平台上多次安装Oracle的系统之类的事情。通过设置ORACLE_HOME,整套oracle命令(从PATH环境变量中加载)然后从该顶级目录下获取设置,定义,映射和库。具有JAVA_HOME环境变量的其他程序(例如java)也是如此。

bash本身具有许多环境变量,这些变量可以更改历史记录(HISTSIZE,HISTFILE等),屏幕大小(COLUMNS),制表符完成(FIGNORE,GLOBIGNORE)语言环境和字符编码/解码(LANG,LC_*),提示(PS1.. PS4)和等等(再次从bash手册页中寻求知识)。

您还可以编写使用自己的自定义环境变量(以传递设置或更改功能)的脚本/程序。


0

“环境变量”是一组动态的命名值,可以影响正在运行的进程在计算机上的行为方式。

它们是运行流程的操作环境的一部分。例如,正在运行的进程可以查询TEMP环境变量的值以发现合适的位置来存储临时文件,或者可以查询HOME或USERPROFILE变量来查找运行该进程的用户所拥有的目录结构。

此处了解更多信息→ http://en.wikipedia.org/wiki/Environment_variable。

您想了解的有关环境变量的所有信息...↑


1
尽管此链接不太可能消失,但最好在此处用相关文本回答问题,并提供该链接作为备份信息的补充。
— Anthon 2013年

@Anthon我相信您是正确的,我会尽快进行更改...感谢您的建议...
— SoCalDiegoRob 2013年

-1

这个答案需要一些shell脚本经验和知识,包括变量,值,变量替换,提示,回显,内核,shell,实用程序,会话和进程。

一个环境变量(ENVAR)是一组全局定义的变量可以影响一个给定的进程将表现在计算机的操作系统的方式。

1.示例性介绍:

我们用en $和大写字母代替envar 。例如:$PS1。

我们可以这样打印envar:

echo $PS1

$PS1保存Unix提示符的值。说它的本机价值是\u \w $。

  • \u 代表(当前)用户,
  • \w 代表工作目录,
  • $ 是边框提示。

所以,如果我们这样做:echo $PS1,我们看到的价值\u,\w再加上年底的美元符号。

如果我们更改该envar的值,则可以在这种情况下更改Unix行为。例如:

PS1="\w >"

现在,提示如下所示(假设工作目录名为“ John”):

John >

我们可以采用相同的方式进行操作PS1="Hello, I'm your prompt >",因此echo $PS1会带来:

Hello, I'm your prompt >

在Bash 4.xx中,我们可以使用以下env命令在系统中打印所有envar 。我建议env在终端中执行并看一下输出。

2.如何显示和处理这些数据:

会话的终端让我们自定义Bash随附的envar。

前面提到的更改通常是暂时的,原因如下:

每个会话(不是子会话)都是唯一的,并且多个进程可以同时唯一地运行(每个进程都有自己的一组envar),但通常从会话0到会话1以及更高版本都有继承。

我们对一个流程所做的更改是该流程所独有的,如果我们在不以某种方式保存它们的情况下将其关闭,则更改将停止。

那么我们如何保存这些更改:

根据我们选择的范围,可以使用几种类型的方法来存储envar更改。以下是此类更改的不同范围(级别):

  • 进程级别:envar仅可用于当前会话中的程序。
  • 出口级:envars可用于在当前会话的程序,或所有其子会话。
  • 全局级别:更改将存储在所有会话中(主要会话和所有子会话)。

envar数据存储在哪里:

Unix由3个主要层构成:内核,shell和实用程序。AFAIK每个shell都有其自己的envar,它们主要或专门在shell中构建。

/etc/profile尽管我们.bashrc当然也可以这样做,但是通常可以在哪些特定位置进行全局更改。

3.创建新的envar:

我们可以创建新的envar,这是一种方法。从Bash 4.xx版本开始,没有命名MESSAGE为本地的enavar (如上所述,envar通常是大写的)。

MESSAGE="Hello world!"

将为我们创建它,现在,如果我们键入echo $MESSAGE,我们将得到hello world!。

如果我们要bash在当前的工作会话(窗口)中执行,则将启动一个新的bash子会话,除非我们执行,否则它将不再在原始进程中工作exit。

注意:在带有终端仿真器的操作系统(例如Ubuntu桌面)中,子会话通常在同一窗口上运行,但是在另一个窗口中的新会话不是现有窗口的子会话(它是相邻进程)。 。

注意:不要在envar值中使用特殊符号,例如!。否则将无法保存。

将envar从原始会话导出到所有子会话:

我们仍然可以使用在第一个会话中创建的envar,以及在第二个会话中创建的envar,而无需将其注册到用户或全局级别的conf文件中(请参阅以下数据)。这样做的方法如下:

转到原始会话(无论是在当前窗口还是其他窗口)并执行:

export MESSAGE

导出时,请勿使用$标志。

现在将其导出到所有子会话。如果您要进行echo $MESSAGE子会话,无论是来自您的用户还是来自另一个用户,都将随后打印该会话。

请注意,PS1不应导出诸如之类的Shell内部变量,但是如果您确实想出于任何原因将其导出并且它们没有出现,则不要在bash之后执行export,而是执行bash –norc。

4. $ PATH envar:

$PATH 用户通常最容易改变的嫉妒。

如果是echo $PATH,我们将看到以下流:

/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games

此envar的打印值在那里用冒号(:)分隔,但这是一种可能更舒适的方式(这些值相同):

/usr/local/bin
/usr/bin
/bin
/usr/local/games
/usr/games

这些是我们运行实用程序时要搜索的目录。

通过执行,which echo我们将获得它的文件位置-例如,我们可能会看到它存在于中/bin/echo。

基于此,我们不必键入echo envar即可查看evnar的值。我们还可以:

/bin/echo $ENVAR

该envar仍将执行,例如:

/bin/echo $HOME

给我们

/home/User || /root

就像:

echo $HOME

给我们

/home/User || /root

注意:$HOME缩写为~。

系统-$ PATH关系以及可能的用户交互:

在Bash 4.xx中,当我们使用没有完整路径的实用程序时,系统将使用上述$PATHenvar的所有6个值。因此,它将从开始/user/local/bin,并将跟随其所有内容以寻找echo可执行文件。

在这种情况下,它将在处停止/bin/echo,在这种情况下,可执行文件将驻留在该位置。

因此,我们可能自定义$PATHenvar的主要原因是安装不在其任何本机值下的可执行文件。

在安装了此类可执行文件之后,我们应该相应地设置它们的$PATH值,然后就可以使用它们了。

5.附录-扩展$PATH:

我们可以export $PATH通过以下方式对子会话进行bash(包括bash扩展,如用于WordPress的WP-CLI或用于Drupal的Drush):

export PATH="/home/John:$PATH"

这将添加一个新的价值/home/John来$PATH,然后用鼠标右键后,将吞并任何原生值了,其中存储了语法下(冒号后右)$PATH。

此类永久更改可以在相关脚本中完成,通常/etc/profile使用名称进行更改.bashrc。


3
这个答案有很多错误:会话和进程的合并,警告!环境变量值不起作用,正好在显示该变量有效的示例下面,错误的子会话概念,关于该做什么的非常奇怪的建议导出shell变量和全局环境变量的错误概念之后。
— JdeBP '18年

warning about ! in an environment variable value not working that is right below an example showing it working?请举个例子。
— JohnDoea

quite bizarre advice about what to do after exporting a shell variable, 你到底什么意思?
— JohnDoea '18年

false notion of global environment variables, 你到底什么意思?
— JohnDoea
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.