【Linux庖丁解牛】—环境变量!
目录
1. 环境变量
1.1 概念介绍
1.2 命令行参数
1.3 一个例子,一个环境变量
1.4 认识更多的环境变量
1.5 获取环境变量的方法
a. 指令操作
b. 代码操作
1.6 理解环境变量的特性
a.环境变量具有全局特性
b.补充两个概念(为后面埋一个伏笔)
1. 环境变量
1.1 概念介绍
• 环境变量(environment variables)⼀般是指在操作系统中用来指定操作系统运行环境的⼀些参数
• 如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪 里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
• 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性
1.2 命令行参数
下面我们先来做一个实验,我们都知道在C/C++中,main函数其实是有两个参数的(int argc,char* argv[ ]),字符指针在C语言中要么存有一个字符的地址,要么表示一个字符串的首地址,argv表示数组中存有字符串,而argc表示字符串的个数。
下面的代码表示将数组中的所有字符串打印下来:
下面是实验结果:
根据结果我们可以发现有某种东西将我们命令行上面的命令(包含空格的字符串)以空格为分隔符分离成多个字符串,并保存在argv数组中。
下面我们再来写一个代码:
#include <stdio.h>
int main(int argc,char *argv[])
{if(argc!=2){printf("usage:%s [-a|-b|-c]\n",argv[0]);}return 0;
}
以上代码是在用户没有正确使用该命令时告诉用户使用该命令的时候需要带上【-a -b -c】其中的一个选项。
所以我们就可以用main函数的命令行参数来实现程序的各种不同子功能【这也是指令选项的实现原理】。
总结:每个进程都有一张argv表,用来实现选项功能,以往我们没有关心main函数的命令行参数是因为我们写的程序不需要这些需求。
小问题:我们在命令行中写的长字符串是被谁切分了呢?
这里直接说结果:用户每次登入系统时都会自动创建一个bash进程,该进程会帮组我们解析命令行中的长字符串。【具体bash是如何做到的我暂时还不知道呢!以后再告诉你们】
1.3 一个例子,一个环境变量
系统预装指令【ls、mkdir】VS 自己写的程序
我们在使用系统级指令时,直接用它们的名称即可。但是当我们要执行自己写的程序时,必须要指明该指令的路径【相对路径或绝对路径】。我们都知道,要执行一个程序,必须先找到它。但是这里有一个问题,系统级指令是如何根据指令名称找到该指令的呢?
先说结论:系统中存在环境变量,来帮助系统找到目标二进制文件!
系统级指令在usr/bin/目录下,如果我们的指令也在该目录下也就可以和系统级指令一样不用指明路径了。
自己写的程序有很多未知性,所以我们还是不要把他放到该目录下!
好,接下来我们来认识一下第一个环境变量PATH
PATH是系统中默认搜索指令的路径。
其中的冒号将每一个默认搜索路径分隔开来,当我们命令行中执行一个命令时,系统会根据PATH环境变量中的默认路径【从第一个路径开始】来查找是否存在该命令。 如果存在,则执行,否则输出该命令未被找到。
既然如此,那如果我们将自己的路径添加到PATH环境变量中,那我们的指令是不是也可以和系统级指令那般直接使用呢?
当然可以,我们直接修改PATH=路径
但是这样系统级指令就找不到了,不过我们关掉xshell重新登入PATH就恢复了
如果我们希望只添加路径的话,如下操作可以办到,当重新登入也会恢复原样:
至于为什么会回复原样,我们需要对环境变量有更加深刻的理解!
接下来,我们从存储的角度理解环境变量。
先说结论,在用户登入系统时,bash进程会形成一张表->【环境变量表】,这张表的结构和命令行参数表完全相同【都是字符指针数组】。
env命令可以查看系统当中的所有环境变量,而环境变量表中存储着这些内容!
所以,环境变量就是bash进程内部malloc的一段空间,而这段空间存储的内容就是一个一个的字符串【环境变量】 。
至此,我们就可以理解当我们在命令行当中敲下ls -al命令时,ls -al的基本执行流程了:bash进程首先拿到“ls -al”字符串,然后切分形成命令行参数表,接着根据解析完成后的字符串到环境变量表当中找到PATH环境变量,根据PATH默认的路径下找该命令,找到即执行,没找到则报错。
那环境变量最开始从哪里来的呢?->[如果不登入就没有bash进程]
先说结论:从系统相关的配置文件中来的!
bash进程创建时就会从配置文件中拿到所有的环境变量并创建一张表!这就是我们上面说到的修改PATH环境变量为什么在重新登入时就会恢复原样的原因。
好,那我们就来看一下什么是配置文件!
在每个Linux用户的家目录下都有一个.bashrc的隐藏文件。
该文件下的100多行都是先关的配置信息。
那如果我们将自己的路径添加到配置文件中的PATH值的时候,是不是每次登入时自己写的程序可以和系统级指令一样使用呢!当然如此!这里就不演示了。
1.4 认识更多的环境变量
。HOME环境变量
指定用户的家目录(即用于登入Linux系统时,默认的家目录)
。LOGNAME | USER 【登入用户 | 用户】
。HISTSIZE
记录我们历史上敲的最新1000行命令
。PWD | OLDPWD [当前工作路径 | 上一次工作路径]
这也是我们使用cd -可以来回切换工作路径的原因,系统已经帮我们记录好了。
最后,我们在回到环境变量的概念:环境变量(environment variables)⼀般是指在操作系统中用来指定操作系统运行环境的⼀些参数
此时,我们就会对这个概念有更加深刻的理解了,我们会发现每一种环境变量都有其特定的功能,有的是记录当前用户的,有的是记录历史指令的,有的是帮我们找指令的默认路径的,还有的是记录主机名,记录当前工作路径……而这些环境变量都是系统提供的,并且这些环境变量本质上是给bash进程使用,这也是间接的给用户使用了!
1.5 获取环境变量的方法
a. 指令操作
上面我们使用过了env【查看所有环境变量】,echo +环境变量名【查看某个环境变量】就不多说了。
export【向环境变量表中导入自己写的环境变量】
unset【取消某个环境变量】
b. 代码操作
1.main函数的第三个命令行参数char *env
我们之前说main函数有两个参数,其实还有第三个参数env,这个参数继承了父进程bash的环境变量表,所以我们也可以通过这个参数拿到所有的环境变量。
因为子进程会继承父进程bash的环境变量表,所以如果我们修改bash的环境变量,那么子进程的环境变量也会随之修改。
bash进程环境变量表修改后:
子进程的环境变量表:
既然如此,bash的孙子进程,曾孙进程等等都会基础bash的环境变量表。
所以说:环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性
2.getenv【系统调用】
给该函数传递环境变量名,该函数就会返回字符串地址【该环境变量的内容】。即可以获得指定环境变量的内容。
上面我们说过:子进程会继承父进程的环境变量,为什么要这样设计呢?
这是为了让我们的进程可以根据环境变量做个性化的操作!下面举一个栗子:
如果我们希望写一个只有我们自己才能运行的程序,该怎么写呢?
我们都知道,系统当中只有bash进程认识当前用户是谁【在环境变量USER当中记录着】,所以我们可以通过获取该环境变量的方式来控制我们的程序逻辑进而实现只有自己登录系统时才能运行该程序的目的。
用户是user2时:
用户是root时:
3. environ 【全局的二级指针】
该指针指向环境变量表的首地址,所以是二级指针。所以我们也可以通过它来获取所有的环境变量。
1.6 理解环境变量的特性
a.环境变量具有全局特性
(前面已经说明过这个特性了,这里就不赘述了)
b.补充两个概念(为后面埋一个伏笔)
1.本地变量
我在命令行中直接定义变量i=10,发现echo命令也可以查看到该变量,但是该变量并不是环境变量,没有记录到环境变量表当中。
但是我们可以用set命令同时查看环境变量和本地变量。
本地变量与环境变量不同,并不会被子进程继承,而只是在bash内部被使用。
2.我们的环境变量是在谁里面?bash!
export是把自己写的环境变量导入到父进程bash里面。
而子进程怎么会导数据到父进程里面呢?这也太奇怪了!
原因就在于export是内建命令【built-in command】执行该指令时,不需要创建子进程,而是让bash自己亲自执行【bash自己调用函数或者系统调用完成】。