1 环境变量是什么

环境变量并不是什么复杂神秘的东西,它就只是一堆“名称”、“值”,“名称”、“值”的键值对而已。每个程序的环境变量是独立的,一个程序只能修改自己的环境变量,不能修改别人的。当一个程序启动另一个程序的时候,被启动的程序会继承启动它的程序的环境变量,启动程序还可以设置被启动程序的环境变量初始值。启动之后,双方的环境变量就彼此独立了。

如果我们想查看单个环境变量的值,在 bash 和 fish 下都可以通过 echo $变量名 打印出来。如果我们想查看当前终端下的所有环境变量,可以运行 printenv 命令。严格来说,printenv 命令并不能输出终端的环境变量,只能输出它自己的。但由于它启动的时候会继承终端的环境变量,因此我们可以认为打印出来的就是终端的环境变量。

2 环境变量有什么用

由于启动一个程序的时候环境变量能传递给被启动程序,并继续传递给它再启动的程序,环境变量就成为了一种程序之间传递信息简单方便的方式。如果某些程序约定了某个环境变量设置了什么值会带来什么效果,那我们就可以通过设置约定的环境变量来在这些软件里实现相应的功能。也就是说,环境变量有什么用,是由被运行的程序约定的。

譬如,Linux 下有很多程序会约定,如果定义了 http_proxy 环境变量,在下载文件的时候就会去连接环境变量指定的代理服务器,如果没有设置,就直接连接,那我们就可以通过设置这个环境变量来让这些程序使用代理。而如果一个程序没有遵守这个约定,那设置这个环境变量就不会有效果。

有一些环境变量是由系统或者终端自动设置的,记录了当前的一些“环境”信息,譬如 USER 记录了当前用户的用户名,HOME 记录了主目录路径,TERM 记录了终端的类型,等等。

3 如何修改环境变量

如果我们不想修改当前终端的环境变量,只想修改被终端启动的单个程序初始环境变量的话,bash 和 fish 都可以使用这样的语法启动程序:

变量1=1 变量2=2 变量3=3 命令 参数

例如:

ANDROID_HOME=/home/edyfox/Android/Sdk ANDROID_SERIAL=ab8888 ./gradlew build

如果我们想修改终端本身的环境变量,在 bash 下,修改当前终端的环境变量可以通过 export 变量名=值 实现。在 fish 下,修改当前终端的环境变量可以通过 set -x 变量名 值 实现。修改了当前终端的环境变量后,在这个终端下启动的程序就会继承修改后的值。

环境变量是每个程序独立的,因此修改了当前终端下的环境变量之后,只会对当前终端有效,关闭终端再打开后修改就无效了。要让修改在新开的终端下也能生效,我们需要让新开的终端再重复设置一遍环境变量。bash 在启动的时候,如果是登录模式会执行 ~/.bash_profile,不会执行 ~/.bashrc;如果是非登录模式会执行 ~/.bashrc,不会执行 ~/.bash_profile,这给我们设置环境变量带来了很大的不便。很多 Linux 发行版修改了默认的 ~/.profile,在 ~/.bash_profile 不存在的时候加载 ~/.bashrc,很多人建议如果编写 ~/.bash_profile 的话,要在里面加载 source ~/.bashrc,然后就假设 ~/.bashrc 一定会被执行,修改环境变量的时候到 ~/.bashrc 尾部添加 export 命令,但这样的假设是不严谨的。更何况 ~/.bashrc 的格式过于自由,里面有可能添加 if 判断等复杂的逻辑,在满足某些条件的时候提前跳过后续命令,因此可靠地为 bash 修改环境变量并不是一件容易的事。即使对 ~/.bashrc 的修改正确,修改也不会对已经打开的终端生效。如果我们想让已经打开的终端也应用这个修改,需要在每个终端里运行 source ~/.bashrc

相比之下,fish 下修改环境变量就容易太多太多了。只要运行 set -Ux 变量名 变量值,fish 就会自动修改记录环境变量专门的配置文件,并且会给所有已经启动的 fish 发消息通知它们加载环境变量,非常方便并且可靠。

4 PATH 环境变量

在 bash 下,PATH 环境变量的值是一个用冒号分割的字符串列表,当用户输入一个不带路径的命令的时候,会按照 PATH 里的路径顺序依次查找命令指定的可执行文件,直到找到为止。譬如,如果 PATH 环境变量的值是 /usr/local/bin:/usr/bin:/bin,那么当用户输入 ls 的时候,bash 就会去查找 /usr/local/bin/ls/usr/bin/ls/bin/ls

然而,在 bash 下修改 PATH 环境变量并没有那么方便,由于 bash 的环境变量并不支持数组,而被看作一个整体的字符串,因此添加、删除、移动某个元素并不是那么方便。如果想把某个路径加到现有 PATH 尾部,修改 ~/.bashrc 加入 export PATH=$PATH:新路径 固然可以实现,但也会造成如果脚本多次执行的话,同一个路径会被反复添加多次的问题。

相比起来,fish 下操作 PATH 环境变量就容易多了,fish 的环境变量支持数组,往 PATH 里添加或删除元素非常容易。此外,想添加一个路径到 PATH 里并且让它永久生效,可以直接使用命令 fish_add_path -U 路径,不需要修改任何配置文件,下次打开 fish 的时候添加的路径就能生效,而且还能通知已经打开的 fish 加载新添加的路径。