linux环境变量
目录
1、见一见环境变量
在windows中就有环境变量,这个环境变量有它的名字也有它对应的值,其实就是一个key:val结构

1.PATH:linux系统的指令搜索路径
在linux中,为什么我们输入指令的时候不需要指明路径,而在执行我们自己写的代码的时候要带上./指明当前路径下的程序,指令其实也就是一个可执行的程序呀,和我们写的代码编译生成的可执行程序有啥区别吗?没有区别。
指令不需要指明路径是因为linux中有一个叫做PATH的环境变量,当我们输入指令的时候,会自动到PATH这个环境变量对应的值下面去寻找。
我们可以使用 echo $PATH,打印一下PATH对应的值,这里$表示要打印的是环境变量,如果不加$打印的就是PATH这个字符串。
![]()
我们可以看到,打印出来的是用:分割开来的很多个路径,当我们输入指令的时候就会默认从这些路径中去寻找,如果第一个路径找不到就去第二个找,所以这就是为什么我们输入指令不需要加路径的原因。
那你怎么证明我们输指令的时候的确去这些路径找了呢?我们直接输入一个不存在的路径,看看它的错误信息是什么。
![]()
给我们的错误信息是命令没有找到,没有找到,说明他曾经找过,但是没有找到,一定程度上可以证明的确是去这个路径下寻找的。
接下来我们还可以把这个PATH的值修改一下,修改之后我们输入命令如果提示找不到,这不就的的确确印证了,指令的确是要去PATH的路径下去寻找的吗?那如何修改呢?PATH=xxxxx,可以直接把PATH的值修改成xxxxx,这里直接会把原来的值覆盖掉。对了,这里修改环境变量的时候不能带空格。带空格会被识别成指令,PATH被当做一个指令 = 和后面的路径会被识别成PATH指令的选项。
![]()

我们把PATH修改为当前的我们用户所在的路径,然后输入ls指令,直接报错说"无法找到该命令,因为/bin:/usr/bin"不包含在PATH环境变量中。我们上面打印未修改的环境变量中的确是有这个路径了,说明我们输入指令是一定要去这个路径中寻找的。
修改之后也不用担心,重启一下xshell就可以恢复。
上面我们把PATH的值修改为我们当前程序的路径,是不是就意味着我们当前的程序不需要带./了呢?是的!

但是,我们直接把环境变量覆盖是很不好的,因为直接啥命令都无法运行了,很多时候我们是希望给PATH添加上我们当前程序的路径,那怎么给PATH添加呢?PATH=$PATH:xxxxx
此时我们可以看到,PATH的值就多了我们当前程序所在的路径,此时执行该路径下的程序也是不需要./的

2.HOME:linux系统用户默认路径
当我们进入xshell的时候,不同的用户使用pwd命令看到的当前目录是不一样的,它怎么知道我是哪个默认目录呢?当我们进入xshell的时候,会自动获取当前用户的信息并把对应的默认路径写入HOME这个环境变量中,此时就知道是哪个用户了。

3.USER:linux系统的用户名
USER这个环境变量就是获取用户名的,所以whoami命令为啥能获取我们用户名呢?因为USER。

4.env:获取系统所有环境变量
那我们怎么获取系统中所有的环境变量呢?直接使用env这个命令即可。

我们知道linux中是有权限的,root用户的权限比普通用户高,但前提是你总要认识我是普通用户还是root吧,现在我们应该知道是如何识别我们是普通用户还是root,原因就在于USER环境变量!当切换不同用户的时候USER和LOGNAME都会变化。
5.getenv:系统调用
我们除了通过命令行的方式获取环境变量,还可以使用代码的方式获取环境变量,getenv只有一个参数,就是要获取的环境变量名,会返回环境变量对应的值。头文件是 stdlib.h

2、什么是环境变量
环境变量就是系统提供的一组name=value形式的变量,不同用户有不同的环境变量,环境变量通常具有全局属性。
在上面我们使用getenv获取了一个环境变量,那如果还要获取全部的环境变量呢?我们先需要知道命令行参数。
1.命令行参数
main函数是可传参的,我们可以给main函数加上两个参数,第一个是int类型,参数名为argc,c可以理解为count的意思,表示个数。第二个是指针数组,参数名为argv,v可以理解为vector,意思是向量,其实就是一个数组。而arg是argument的缩写,第一个参数argc就是第二个指针数组的元素个数,因此我们可以直接打印出argv这个数组里都有啥元素。

我们可以看到,这个数组里其实就是把我们在命令行上输入的内容打印了出来。
argv这个数组里的元素,是将我们命令行上输入的命令以空格为单位,打散成多个字符串,依次放入到argv里。
现在问题来了,为什么要打散呢?
我们写一个代码就懂了。假设我们argc不能等于2,所以需要的就是指令后面带一个东西。如果你不等于2,给你打一个提示信息。

看到这里,我们应该就可以明白了,这样做的原因是为指令、工具,软件等命令行提供选项的支持!!。我们使用的ls后面带的指令是啥呀,不就是对ls后面的指令做解析,然后执行不同的功能吗?我们的指令大部分都是用C/C++写的,为了支持命令行参数,就需要对main函数传参。

如果不需要传参那你就不用带。
在windows中,不就也有命令行参数吗?

其实不光C语言要有命令行参数,JAVA/python/PHP,几乎任何语言都要支持命令行参数,因为这样就可以定制化功能。
那main函数除了有int argc, char* argv[],还有其它参数吗?main函数还可以带第三个参数,char* env[],,这个env是一个环境列表,我们可以使用它获取到系统当中的全部环境变量。我们可以打印出来看看。这个环境列表和argv是一模一样的,在数组最后一个元素后面为带上一个NULL,所以我们在打印的时候,可以循环结束条件直接就env[i],当为NULL的时候自动结束。
int main(int argc, char* argv[], char* env[])
{
int i = 0;
for(; env[i]; i++)
{
printf("env[%d]->%s\n", i, env[i]);
}
return 0;
}

我们可以看到果然把环境变量打印出来了。 因此,一个进程加载到内存的时候,会被传进来两张表,一张就是命令行参数列表,一张就是环境列表。
并且我们会发现,这个打印出来的环境变量,和我们用env打印出来的环境变量是一样的,我们写的进程没有环境变量,但是启动之后会被传进来环境变量列表,是谁给我们传的呢?我们在运行程序的时候会变成一个进程,这个进程是bash程序的子进程,bash程序就是我们使用的xshell,因此,我们写的程序的环境变量是bash传给我们的。
也就是说,我们所运行的进程,都是子进程,bash本身在启动的时候会从操作系统的配置文件中读取环境变量,子进程会继承父进程交给我的环境变量!
那如果我们在我们的程序里面创建子进程,子进程也可以继承父进程的环境变量,就是说以bash为起点,环境变量可以被下面的所有子进程继承,也就是说,从bash开始,所以命令行上运行的程序都认识bash的环境变量,所以环境变量具有全局属性,因为可以被下面所有进程继承下去。
那么我们怎么验证呢?怎么证明环境变量是可以被子进程继承的?
我们可以新增一个环境变量,然后看看子进程是否可以打印出来我们新增的环境变量。
2.增加环境变量
我们知道怎么查环境变量了,怎么修改环境变量了,但怎么增加呢?
直接xxx=xxx,比如说我们直接在命令行上输入MY_NAME=MRL。

可以我们用env打印不出来呀,我们创建的环境变量的确是存在的, 只不过没在环境列表里,因为直接在命令行里定义的变量叫做本地变量。如果要把本地变量变为环境变量,需要在定义的时候加一个export。

我们把这个环境变量导入到了bash里面, 这个和我们上面写的打印环境变量的程序是没有关系的,现在我们直接执行一下这个程序,看看打印出来的环境变量是否有我们新增的。

的确打印出来了,这就验证了子进程是会继承父进程的环境变量的!可以被子进程孙子进程、兄弟进程全部继承。如果我们想要让所有进程遵循某条规则直接新增环境变量即可。
3.unset取消环境变量
直接unset+环境变量名,一旦取消,所有子进程都看不到了。

下面问题就来了,main函数既然有参数,那为啥我们不带参数的时候不会报错?因为是编译器给我们加的,如果我们写的main函数里面带参数了,编译器编译的时候就会把父进程的环境变量啥的传给main函数,如果没有,就不会传。
4.本地变量 && 内建命令
本地变量就是我们直接在命令行参数上定义的,但是我们用env是查不到本地变量的,上面已经验证过了。
![]()
那我们怎么查包括本地变量和环境变量所有的变量呢?使用set,set可以查到所有的变量。
本地变量是不会被子进程继承的,只在本bash内有效,我们使用set打印出来的是非常多的,这里只截取了我们刚刚定义的本地变量,大家可以自己在xshell打印出来看看。

验证一下本地变量不会被子进程继承。

为什么bash需要用到本地变量呢? 因为有一些符号只在本bash内有效,比如说 \,这个符号。这个符号在bash内可以起到续行的作用。
![]()
命令行输入的指令全部都是bash的子进程,子进程是不会继承父进程bash的本地变量的。上面我们定义了一个本地变量NAME=VAE,然后用ehco $NAME,这条指令打印出来了NAME的值VAE。这不矛盾了吗?NAME是bash定义的本地变量啊,echo命令创建的进程是bash的子进程啊,子进程无法继承父进程的本地变量,我们自己写的代码都拿不到NAME的值,凭啥你echo就能拿到NAME的值呢?我们可都是bash的子进程啊。
![]()
我们命令行上启动的指令不一定都要创建子进程,有些进程是bash自己去执行的。所有命令分为两批命令:1.常规命令——通过场景子进程完成的,2.内建命令——bash不创建子进程,自己亲自执行,类似于bash调用了自己写的或者系统提供的函数。
所以echo命令是怎么实现的呢?是bash自己执行的,所有echo才可用拿到自己本地变量。
与之类似的还有一个cd命令,每个子进程都有自己的工作目录,如果cd的执行创建了子进程,那改的路径都是子进程的路径,父进程的路径不应该受影响,但其实父进程的路径是会被影响的,原因是cd命令是个内建命令。那cd命令是怎么执行的呢?系统中有个接口 chdir。

直接传递一个路径,就会把当前工作路径修改为传递的路径。
我们可以自己写一个cd 命令类型的功能。
int main(int argc, char* argv[], char* env[])
{
printf("change before\n");
sleep(30);
if(argc == 2)
{
chdir(argv[1]);
}
printf("change after\n");
sleep(20);
return 0;
}

我们先休息30秒,然后我们查一下该进程的pid,然后进入到cwd,就是当前工作路径下查看该进程的工作路径,没有chdir之前工作路径还是当前路径。chidr之后,路径就变为了我们命令行的第二个参数,也就是我们传递的 /,也就是根目录。
3、总结
本文讲了什么呢?
1.环境变量是什么
name=value形式的变量,环境变量是具有全局属性的,为啥能具有全局属性呢?因为可以被所有子进程继承。
2.见了见linux都有哪些环境变量
PATH、HOME、USER等等。
3.如何修改某个环境变量呢?
直接[变量名=修改的内容],这是覆盖式写入。
如果要添加[变量名=变$量名+新增的内容]。
4.如何查看一个环境变量的值?
命令行方式:echo $变量名
代码方式:getenv(变量名)。
5.如何查看所有的环境变量?
命令行方式:env
代码方式:使用命令行参数
6.命令行参数是什么?
给main函数传参,有3个参数,argc,argv,env,具体什么含义呢?这三个参数名不是必须是这三个,只不过一般情况都使用这三个参数名,因为见名知义,很好记忆。
7.如何新增环境变量?
直接命令行上xxx=xxx,在使用export,导成环境变量。
8.什么是本地变量?
直接命令行上xxx=xxx,就是本地变量,本地变量子进程继承不了。
9.查看本地变量和环境变量
set命令
10.取消环境变量
unset [环境变量名]
11.什么是内建命令?什么是普通命令
bash自己执行的命令就是内建命令。
bash通过创建子进程执行的命令是普通命令。
所以为什么我们写的代码包含的头文件可以找到它在哪个位置呢?不就是环境变量吗?为什么一些python、java可以找到对应的解释器呢?因为你装了环境变量啊!
更多推荐
所有评论(0)