你不知道的环境变量

栏目: Node.js · 发布时间: 4年前

内容简介:编者按:本文作者李松峰,资深技术图书译者,翻译出版过40余部技术及交互设计专著,现任360奇舞团Web前端开发资深专家,360前端技术委员会委员、W3C AC代表。提到环境变量,我们都知道但是,除了这些,你对环境变量还知道多少?本文将带领大家全面了解一下环境变量。

编者按:本文作者李松峰,资深技术图书译者,翻译出版过40余部技术及交互设计专著,现任360奇舞团Web前端开发资深专家,360前端技术委员会委员、W3C AC代表。

提到环境变量,我们都知道 PATH 中包含着常用可执行文件的路径,有了它在命令行程序中直接输入文件名就可以运行程序。在Node.js环境中,我们也经常使用 process.env.NODE_ENV 来区分开发和线上环境,比如在开发环境下可以打印日志、不压缩资源,以方便调试。

但是,除了这些,你对环境变量还知道多少?本文将带领大家全面了解一下环境变量。

一点背景

我们现在使用的环境变量是在1979年Version 7 Unix中成型的,此后所有Unix系统包括 Linux 和macOS都实现了同样的特性。1982年,从PC DOS 2.0起,所有Windows操作系统也包含了环境变量,只是语法、用法和标准变量名有所差异。

环境变量是与进程紧密相关的一个特性。进程是什么?通俗地理解,进程就是“进行中的程序”,书面说法则是“运行中程序的一个实例”。意思其实都一样。之所以要抽象出进程这个概念,部分原因是为了隐藏使用CPU和内存资源的复杂度。有了进程的概念,一个程序在运行时就好像可以占有全部CPU和内存一样,不必考虑其他同时运行的程序。而每个程序都是在一定的上下文中运行,这个上下文中包含了程序正确运行所需的状态。这里所说的状态包括程序的代码、数据、栈、通用寄存器的内容、程序计数器、环境变量及打开的文件描述符,等等。

访问环境变量

环境变量可以在脚本中使用,也可以在命令行中使用。通常需要在变量名前面或两侧添加特殊符号来引用某个环境变量。比如,要显示用户的主目录,在大多数脚本环境中必须使用:

echo $HOME

在DOS/Windows命令行解释器(如 cmd.exe )中,要这样写:

ECHO %HOME%

在Windows PowerShell中,则要这样写:

Write-Output $env:HOMEPATH

命令行程序有3个内置命令,可以列出环境变量及它们的值:

  • env

  • set

  • printenv

在Unix和类Unix系统中,环境变量区分大小写。

fork,exec

在Unix中,环境变量通常在系统启动时由初始化脚本进行初始化,然后由系统中的所有其他进程继承。同样,当在一个程序中打开另一个程序时,调用程序会先复制一个与自身完全一样的进程,即子进程。子进程可以根据需要修改环境变量。最后,子进程再通过执行被调用的程序来覆盖自己。其中,复制进程对应 fork ,执行程序对应 exec

  • fork:是由操作系统内核实现的系统调用,用于创建当前进程自身的一个副本;

  • exec:同样是由操作系统内核实现的系统调用,用于在已有进程的上下文中运行一个可执行文件。

exec在实际实现中通常是一组函数的代称,比如在 C语言 中就有 execlexecleexeclpexecvexecveexecvp 等几个函数,它们共用 exec 这个名字,只是分别在末尾追加了一两个字母,表示自己接受的参数不同(比如, e 表示接收环境变量的指针数组, l 表示一个一个地接收命令行参数,即参数列表, p 表示使用 PATH 环境变量查找文件, v 表示接收命令行参数的指针数组或者叫向量)。Linux内核只有一个叫 execve 的实现,前面所有其他的函数都是在用户空间中对这个系统调用的封装。

下面我们通过分析 execve 来理解父进程在创建子进程时如何传递环境变量。先看一看 execve 的接口:

#include <unistd.h>

int execve ( const char * pathname , char * const argv [ ] ,

char * const envp [ ] ) ;

(http://man7.org/linux/man-pages/man2/execve.2.html)

execve 函数接收3个参数,第一个是可执行文件的路径 pathname ,第二个是参数的指针数组 argv ,第三个是环境变量的指针数组 envp 。下图展示了参数数组的数据结构:

你不知道的环境变量

如图所示,变量 argv 指向一个NULL结尾的指针数组,前面的每个元素都是一个指向参数字符串的指针。按照约定, argv[0] 是可执行文件的名称。下面是环境变量指针数组的数据结构:

你不知道的环境变量

两上数据结构类似。唯一的区别是,环境变量数组元素指向的字符串都是名-值对形式的,比如 "PWD=/usr/droh"

在找到 pathname 对应的可执行文件后, execve 会调用操作系统永驻内存的 loader 代码,把可执行文件的代码和数据从磁盘复制到内存。然后,跳到其第一个指令或“入口点”开始执行该程序。这个过程叫加载。

加载之后,就是通过系统启动函数来运行用户的 main 函数:

int main(int argc, char *argv[], char *envp[]);

同样, main 函数也接收3个参数,其中最后一个参数 envp 就是新进程或子进程继承的环境变量。

命令行程序

命令行程序是我们最常用的启用其他程序的工具,因此会频繁地使用上述的 fork / exec 过程。比如:

node ./index.js

就会启用一个新的Node.js进程,运行index.js。与此同时,父进程的环境变量也会传递给这个新的子进程。

在Unix中,脚本或编译的程序修改的环境变量只会影响当前进程及其子进程。父进程及其他不相关的进程不受影响。

在命令行程序中,内置的 export 命令用来在当前进程中创建环境变量(自然也会被子进程继承):

export API_URL=http://example.com/api

不使用 export 关键字也可以创建变量,虽然以这种方式定义的变量可以通过 set 命令显示,但却 不是 真正的环境变量。这种变量只是命令行程序存储的,操作系统内核并不认。因此 envprintenv 命令不会显示它们,子进程也不会继承它们:

API_URL=http://example.com/api

然而,在命令行中调用其他程序时,在前面添加类似上面的变量赋值,则会将该变量添加到子进程的环境变量中:

API_URL=http://example.com/api node ./index.js

这样,index.js就可以通过 process.env.API_URL 取得传入的API地址了。

用户可以在自己所用的命令行 工具 的配置脚本(profile script)中添加或修改环境变量。

在DOS和Windows命令行解释器中,使用 SET 命令创建环境变量并赋值:

SET VAR_NAME=VALUE

只有 SET 命令会显示所有环境变量及它们的值。

hashbang

命令行脚本除了可以直接使用环境变量,还有一种常见的用法,就是“ hashbang ”。比如,下面这个脚本使用Node.js作为解释器:

#!/usr/bin/env node

console . log ( 'Hello, You Got It!' )

第一行中的 #!/usr/bin/env 是命令行程序内置 env 命令的完整路径,后面跟着运行当前脚本的程序名 node 。意思是在 env 的环境变量 PATH 中去查找解释程序 node

当然,不使用 env 而直接给出 node 的路径也是可以的:

#!/usr/local/bin/node

console . log ( 'Hello, You Got It!' )

但这样是不是就不灵活了?毕竟不同环境下 node 的位置可能不一样。使用 env 就可以做到在运行时再定位解释器的位置,从而让脚本的兼容性更好。

当然,使用 env 也有缺点:不同机器上 env 的位置同样可能不一样!

[完]

参考文献

  • https://en.wikipedia.org/wiki/Environment_variables

  • https://en.wikipedia.org/wiki/Env

  • Computer Systems A Programmer’s Perspective Third edition

关于奇舞周刊

《奇舞周刊》是360公司专业前端团队「 奇舞团 」运营的前端技术社区。关注公众号后,直接发送链接到后台即可给我们投稿。

你不知道的环境变量


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

精通Windows应用开发

精通Windows应用开发

[美] Jesse Liberty Philip Japikse Jon Galloway / 苏宝龙 / 人民邮电出版社 / 59.00元

Windows 8.1的出现不仅提供了跨设备的用户体验,也提供了跨设备的开发体验。本书着眼于实际项目中所需要的特性,以及现有C#编程知识的运用,对如何最大限度地利用Metro、WinRT和Windows 8进行了讲解,内容详尽,注重理论学习与实践开发的配合。 Windows 8.1和WinRT的作用及其特殊性 如何使用先进特性创建具有沉浸感和吸引力的Windows 8.1应用 如......一起来看看 《精通Windows应用开发》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具