目录
一、基本概念
环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,如:我们在编写C/C++代码时,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。环境变量通常具有某些特殊用途,在系统当中通常具有全局特性。
二、环境变量的种类
和环境变量相关的命令:
- echo:显示某个环境变量值
- export:设置一个新的环境变量
- env:显示所有环境变量
- unset:清除环境变量
- set:显示本地定义的shell变量和环境变量
输入如下指令,可以看到系统的环境变量:
env
以上所有环境变量都是当前用户登陆时,Linux为当前用户定制的。
其中常见环境变量有如下几种:
- PATH:指定命令的搜索路径
- HOME:指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
- SHELL:当前Shell,它的值通常是/bin/bash。
我们可以通过以下指令来查看环境变量:
echo $[环境变量名称]
1、PATH
我们所写的代码编译生成可执行程序后,为什么在运行时必须要加上 ./ 呢?为什么同为可执行程序的 ls 、 pwd 等等诸多指令在使用时不需要加 ./ 呢?尽管他们在本质上完全相同。
写如下代码进行观察:
编译运行,现象如下:
之所以要加上 ./ ,是因为在程序运行时,需要说明该可执行程序所处的路径,执行一个命令的前提是先找到它。如果我们愿意,也可以使用绝对路径来说明:
接下来回答第二个问题,为什么同为可执行程序的 ls 、 pwd 等等诸多指令在使用时不需要加 ./ 来说明其所处路径呢?这是因为在系统中存在一个环境变量帮助我们在特定路径下搜索这些默认指令,这个环境变量叫做 PATH 。
使用指令 echo $PATH 来查看该环境变量:
可以发现在环境变量 PATH 中指明了由 ":" 分割开来的若干个路径,我们在执行命令时,系统会自动在这些路径中从前向后依次寻找,找到了就自动执行,而不需要人为说明其所在路径。
使用 which 指令来查看部分系统默认指令所处的路径:
可以看到 ls 与 pwd 的路径刚好被包括在 PATH 之中。
解决了如上两个问题后,我们该如何让自己编写的可执行程序在运行时也不需要加上其路径呢?很简单,只需要把自己的可执行程序的路径添加到 PATH 中就可以了,使用命令:
export PATH=$PATH:[自己的程序的路径]
如此一来,便可以直接执行自己的可执行程序了。环境变量会在xshell重新打开时被恢复初始状态,所以大家也不用担心在设置环境变量时出现错误。
除了这一种做法以外,我们还可以把自己的可执行程序拷贝到 PATH 中本来就有的路径当中去,达到相同的效果,这里不再演示。
实际上,在Linux中,把可执行程序拷贝到系统默认路径下,让我们可以直接访问的方式,就相当于Linux下软件的安装。把可执行程序从系统默认路径下删除,就相当于软件的卸载。
2、HOME
HOME 环境变量代表当前登录用户的家目录,对比普通用户与root用户的 HOME 环境变量:
发现 HOME 环境变量是不同的。这说明对于不同的登录用户,同一个环境变量可能存放不同的数据,即环境变量是针对特定的人,在特定的场景中被使用的变量。
三、获取环境变量
main 函数的形参列表最多可以带有 3 个参数,分别为 argc 、 argv 、 envp :
int main(int argc, char* argv[], char* envp[])
{}
其中 char* envp[] 是一个指针数组,该数组里面的指针都分别指向不同的字符串,并且最后一个指向有效字符串的指针的下一个指针一定指向 "NULL" 。
构成了上图所示的表结构形式。
编写如下程序:
编译运行:
发现这些指针指向的就是一个一个环境变量字符串。
讲到这里,我们的C/C++素养让我们意识到当函数传参数组时,传递的不是数组本身,而是数组首元素地址,所以在 main 函数的形参列表中写成的 char* envp[] 形式,本质上是一个二级指针,名为 environ ,查看一下 man 手册:
man environ
最终,可以得知这个表状结构的具体示意图为:
以后想要获取环境变量就可以通过遍历这个表状结构体获取了。
但是这样做的话太过于麻烦,为了方便起见,主流的获取环境变量的方法是通过函数获取,该函数名为 getenv 。
使用 man 手册查看该函数:
man getenv
可知其用法为:
getenv("[环境变量名]")
同学们可以编写如下代码实际操作一下:
编译运行:
可以得到预期的结果。
四、深入理解环境变量
有了以上知识,我们可以知道环境变量本质上就是内存级的一张表,这张表在用户登录系统的时候,由系统给该用户自己单独形成。每一个环境变量都有自己的用途,有的是进行路径查找的,有的是进行身份认证的,有的是进行动态库查找的,有的是用来确认当前路径的等等。每一个环境变量都有自己的特定应用场景。
其中,环境变量对应的数据,都是从系统的相关配置文件中读取进来的,我们进入自己的家目录,查看该目录下的所有文件:
可以看到两个隐藏配置文件,分别叫做 .bash_profile 与 .bashrc ,打开 .bash_profile ,观察内容:
可以发现这里就有着环境变量 PATH ,当用户登录的时候,程序就会自动执行当前用户下的配置文件,加载完成后,环境变量也就被添加进来了。用户每次登录,都会重新加载一遍配置文件。
除了用户自己的环境变量,还有一些全局的环境变量是在 /etc 路径下的配置文件 .bashrc 中,在系统启动时添加的,这里暂时不做介绍。
我们知道,当用户登录机器时,操作系统会给用户创建一个 shell ,用来为该用户提供命令行解释。 shell 本身是一个进程,会在 shell 中维护上面我们所提到过的环境变量的表状结构。
用户在执行命令时,都是命令行解释器 shell 帮用户执行的,对应到 Linux 中,是 bash 在执行。 bash 除了可以执行命令外,还可以命令行式的自定义变量:
在命令行中写下指令 myval=100 后,shell读取到指令,就会在内存中申请一块空间,并把该变量以字符串 "myval=100" 的形式存放进去,最后在shell内部另外生成一个指针指向该字符串。
如果在命令行前面加上了指令 export ,那么就会换把表状结构中的空余指针指向该字符串:
再使用指令 env 查看一下环境变量表:
我们新增的环境变量 holle 正在其中。
由于所有在命令行中执行的命令都是shell的子进程,所以当我们以后执行命令时,shell就会给这些子进程传参,使子进程拿到这个表状结构。
写一个程序来证明一下:
编译运行:
可以看到该子进程确实读取到了刚刚新增的环境变量 hello 。所以环境变量是可以被所有的子进程继承的,即环境变量具有全局属性。
我们之前定义的 myval 也是在命令行中定义的,只是前面没有加上 export ,所以 myval 虽然存在,但是并没有被导入表状结构中,这种变量被称为本地变量。本地变量只在shell内部有效,不被子进程所继承。
而在命令行输入指令 echo $myval 时,shell创建的子进程却能够获得该本地变量并把它打印出来,这涉及到另一个概念:内建命令。这部分内容我以后会进行详细讲解。
与环境变量有关的内容就讲到这里,希望同学们多多支持,如果有不对的地方欢迎大佬指正,谢谢!