什么是环境变量

主流的操作系统 Windows、Linux、MacOS 都有环境变量的概念。

打开 Windows 10 中的环境变量设置,如下图所示,
主流的操作系统 Windows、Linux、MacOS 都有环境变量的概念。

打开 Windows 10 中的环境变量设置,如下图所示,

pkzlJRP.png

这些环境变量本质上都是存储在 Windows 系统注册表里面的。

环境变量可以理解为 影响 程序运行的一些 信息配置项 。英文叫 Environment Variables,下文中,有时我会简称 env

每个环境变量都是一个配置项,提供一个信息。

看到这些环境变量,很容易对环境变量产生误解,以为它们就是用来配置路径的。

不一定都是这样!

环境变量可以用来配置路径,也可以用来配置其他,比如上图中的 NUMBER_OF_PROCESSORS,指明了系统中处理器核心是4个。

如何产生的

一部分是由程序创建、修改 的,

一部分是由人手工创建的。

程序可以通过操作系统编程接口 增加、读取、删除、修改 环境变量配置。

人也可以通过命令或者操作界面 增加、查看、删除、修改 环境变量配置。

有什么用?

主要是为应用程序提供信息, 程序可以根据这些信息,决定它们的行为。

因为某些程序会读取一些环境变量的值,根据这些值决定程序的处理。

举个例子,上文中的 NUMBER_OF_PROCESSORS,是操作系统程序创建的,目的就是提供信息给 系统中所有的程序, 告诉他们当前系统所在计算机的CPU核心是4个。

那么如果有一个程序需要给客户显示系统中有多少个CPU,就可以读取这个数据,显示在界面上。

更常见的,就是指明一些程序文件或者数据文件所在的目录。比如 path、ANDROID_HOME 。

从上面的描述,可以思考出这样一个结论:

到底哪些程序 会 受哪些环境变量的影响, 是由程序本身决定的。

程序的代码 到底会 读取哪些环境变量,根据这些值做出什么行为,完全是程序开发者设计决定的。

环境变量本身只是一些配置信息。

path环境变量

环境变量中,我们接触最密切的恐怕就是 path 这个环境变量了。

因为很多程序都会根据它的值 决定自己的行为。

最典型的就是 命令行解释器(运行在命令行窗口中的), 有时也叫shell程序。

就是解释执行我们从 命令行窗口(术语是伪终端模拟器)输入命令的程序。

当我们在命令行窗口敲入如下一行指令的时候

1
python.exe hyhy.py

shell程序会接收到这行命令, 它会以空格作为分隔符,把指令分割为n个部分,第一个部分就是要执行的程序,后面的都是这个程序的参数。

那么第一个问题就是, 到哪里找这个程序 python.exe 呢?

这就是根据环境变量 path 的值决定的。

它会依次到环境变量 path 里面指定的目录下面, 一个个的找。

先在哪个目录下面找到了python.exe,就执行哪个目录下面的 python.exe。

不仅仅是 shell 要使用 环境变量 path, 很多其他程序也会使用,比如 Windows 操作系统寻找dll文件时,path里面指定的目录也是搜索路径之一。

看到这个例子,有些朋友可能又有误解了。

认为只要是配置目录的,都像 path 那样,可以配置好几个目录给程序依次查找。

不是的!!

还是那句话,怎么使用 环境变量 要看 各个程序本身的设计。

比如 环境变量 ANDROID_HOME, 就是只能包含一个目录的。

修改环境变量配置

如果一个程序用到Windows 系统配置里面的环境变量, 当我们修改了 系统配置里面的环境变量,注意,一定要重启这个程序。

比如:cmd命令行解释器。

如果你修改了环境变量path,比如添加了一个目录,注意一定要重启cmd。

因为cmd启动后,就保留了一份自己的环境变量,这是再去修改 配置环境变量,不会影响已经启动的cmd程序。

如果要使修改生效,必须重启cmd程序。

环境变量的继承

我们再来看一个例子, 刚才我们说,程序重启后,系统配置里面的环境变量就会生效。

我们再通过一个程序来看一下。

大家在集成开发环境 Pycharm,先运行一下,下面的Python程序

1
2
import os
print(f'环境变量 byhy :{os.environ['byhy']}')

可以发现,程序会报 KeyError: ‘byhy’ 错误,因为我们并没有这样的一个环境变量。

现在,我们去修改 系统配置,添加这个环境变量。

然后,在 重新运行程序(就像重启一样),你会惊奇的发型,仍然会报错。

为什么?不是重启程序,修改后的环境变量就会生效吗?
其实,每个程序启动后(运行的程序叫做进程),就会自己拷贝 一份 父进程 环境变量表,作为该 进程 的环境变量表。

父进程 通俗的说 就是 启动了这个程序的 程序。

我们是在 Pycharm 里面运行 Python 程序, Pycharm 就是 Python 程序的父进程。

如果我们在 命令行cmd 中运行 Python 程序, 命令行cmd 就是 Python 程序的父进程。

那怎么解决这个问题呢?

连 Pycharm 也重新启动一下,这样 Pycharm 的 环境变量也更新了,它的子进程当然也会使用更新后的环境变量 就可以了。

那 pycharm 和 cmd 的父进程又是谁呢? 是 Windows 桌面管理器 explorer 。

explorer启动时,会从注册表中读取 环境变量配置作为自己进程的环境变量。

后续用户启动的程序 大都是 直接 或者 间接的从 explorer继承的环境变量,当然也就使用了配置的环境变量。

前面的例子,我们重启 cmd窗口,可以有效的重新加载系统配置的环境变量,是因为 cmd窗口的父进程是 explorer。

explorer每次启动新的进程,会重新读取 配置环境变量, 作为子进程的环境变量。所以重启cmd就有效了。

但是 cmd 或者 pycharm 启动子进程, 并不会 重新读取 配置环境变量,而是把自己的环境变量作为 子进程的环境变量。 当然,修改的配置不会生效。

那么是不是 只有explorer 启动的子程序,才能使用最新的 配置环境变量呢?

也不是,要看程序的设计,完全可以启动子程序时,重新读取 系统配置里面的环境变量给子程序使用。

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 代码文件 e1.py
import os,subprocess

def printEnv(filename):
with open(filename,'w') as f:
for k, v in os.environ.items():
f.write(f'{k}:{v}\n')

os.environ['A1'] = '白月黑羽1'
printEnv('e1.txt')

subprocess.Popen(
[r'c:\Python37\python.exe',r'h:\tmp\e2.py'],
shell=False
)
1
2
3
4
5
6
7
8
9
10
# 代码文件 e2.py
import os

def printEnv(filename):
with open(filename,'w') as f:
for k, v in os.environ.items():
f.write(f'{k}:{v}\n')

os.environ['A2'] = '白月黑羽2'
printEnv('e2.txt')

运行 e1.py 后, e1.py的代码会启动 e2.py

所以程序运行时, e1.py 就是 e2.py 的父进程。

上面的程序运行后,可以发现 e2.py记录的 环境变量 是在 e1.py的基础上多一个 A2=白月黑羽2 。

可以证明:环境变量从父进程继承而来。