有了我们前面的命令行参数的理解基础呢,我们下面进入环境变量这一个部分的内容的学习。
一般在我们安装一些开发工具尤其是有解释器的开发工具的时候,我们呢一般都要配置环境变量,可能都不太清楚自己为什么要配置环境变量?
一般呢,大家稍微注意一下就能够知道一般我们都会给什么Java的JDK啊,python的解释器,或者eclipse,IDEA进行配置环境变量。
那么我们前面写了个我们自己的touch命令发现我们自己写的touch命令在运行的时候需要带上./mytouch才能运行,而系统命令是自己输入命令+选项就可以直接运行了。
其实这就是跟我们的环境变量有关系了。
首先要执行我们的命令,就必须要先找到命令所对应的可执行程序!
而之所以我们运行我们自己写的touch命令的时候需要带上./的原因就是告诉系统,我们要执行的可执行程序就在当前目录下,其实我们的系统命令为什么没有带上找到他的路径呢?其实系统命令也可以通过给出搜索路径来进行执行命令,那么不用带搜索路径说明系统就一定有它默认的搜索路径
我们发现系统命令也可以带路径执行,也可以不带路径执行,而我们自己的可执行程序不带./的话发现我们的bash找不到该命令。
所以呢,在Linux当中会有一个环境变量通过指令echo $PATH查看环境变量,可以记录我们的可执行程序的搜索路径。
打印出来了一长段的字符串,每一个子串其实就是可执行程序的搜索路径,这些可执行程序的路径都是以:作为分隔符,那么假设我们输入一条命令ls,系统会以:作为分隔符在这些搜索路径中进行查找是否有对应的可执行程序,如果找到了那么当然就可以直接运行了,如果没有找到那就会出现如下一条提示:
命令没有找到。
那么我们如何添加这里的环境变量呢?
通过输入命令:PATH=$PATH: 可执行程序的搜索路径
例如:
发现我们的可执行程序搜索路径配置好了环境变量,此时可以直接输入mytouch命令可以不用再带./,当然带上./也能正常运行,这样就可以理解为什么系统指令不用带路径执行了。
我们可以看到通过配置之后,指令可以正常运行了。
那么如果我们不想要该环境变量怎么办呢?
可以通过给PATH重新赋值:
此时我们的环境变量又改过来了,我们运行mytouch 可执行程序发现:
又只能用./方式运行了,因为我们没有给该可执行程序配置环境变量,也就是可执行程序的默认搜索路径。
在我们的xshell当中,默认更改环境变量只限于本次登录,重新登陆环境变量会被自动恢复。
下面我们修改代码为如下所示:
我们只对可执行程序进行打印就行了。
我现在呢不想把我们的可执行程序的路径添加到我们的环境变量里,我们就想把我们的可执行程序给拷贝到我们的系统路径下。下面通过执行如下操作就能够将该可执行程序拷贝到系统路径下:
这种操作需要提高你的权限所以前面我们需要加一个sudo,执行完之后我们which mytouch就可以看到我们的可执行程序就被成功的拷贝到系统目录下了:
然后我们执行mytouch命令:
这样呢跟我们所执行的ls系统命令就没有什么区别了,就不需要带./,可以用which来查看该命令所在的路径了。像这种操作我们可以把它称之为程序安装。
上面我们执行上述sudo rm /usr/bin/mytouch把该可执行程序从系统目录中删除就叫做程序卸载。
我们发现再去执行该命令就执行不了了。
现在呢我们改环境变量只是临时的去修改环境变量,那这些环境变量一开始是从哪里来的呢?我们还没开机呢?我们也会进行重启啊,那么PATH里为什么就默认有那些路径呢?那如果我想要让我们的环境变量永久有效呢?
所以,我们的环境变量并不是单纯的在内存中创建的,它一定是从某些配置文件中去读取的,其实我们的系统中呢会存在很多的配置文件,有一类配置文件是在我们命令行启动的时候会把我们预设的环境变量和变量的值给我们导入进来的,所以为什么我们改了之后会重启我们的xshell之后他又会失效,原因就在于重新登录之后系统是会重新从配置文件中去读取这些环境变量的。
下面我们再认识一个环境变量:PWD
系统他怎么知道我们在当前哪个路径呢?我们执行pwd能够将我们当前所在的路径打印出来,然后我们进入上一级目录之后再pwd发现打印的路径也随之改变了。
其实呢系统中有这么一个环境变量:PWD专门为bash内置的用来记录我们当前所处的路径的一个字段,而我们的pwd命令只需要读取PWD将其打印出来就可以了。
下面我们再说一个环境变量:HOME
为什么普通用户所处家目录默认是/home/XXX呢?
为什么root用户所处的家目录默认是/root呢?
原因就在于系统中一定有一个环境变量,这个环境变量比我们登录还要早,在我们登陆的时候呢可以识别出我们的用户,因为我们登录的时候是输入了用户名的,然后可以识别到我们是普通用户还是root用户。然后形成对应的环境变量。然后根据用户名初始化我们的环境变量。
登录的时候:
1.输入用户名和密码
2.认证
3.认证通过后会形成环境变量(肯定不止一个,PATH,PWD,HOME).
3.1根据用户名初始化HOME=/home/XXX,HOME=/root
4.cd $HOME
上面我们已经了解了三个环境变量PATH,PWD,HOME.
其实环境变量有很多,那么我们如果想要查看系统默认初始化的所有的环境变量我们该如何查看呢?
我们可以通过执行命令:env
可以看到很多的键值对的形式“=”左右两边分别为环境变量的键和值的形式显示。
目前呢我们当前是普通用户,我们可以看到目前USER是这个,那么当我们切换用户为root之后:
那么我们的环境变量就被初始化成了root.
我们的系统中会存在大量的环境变量,每一个环境变量都有它自己的特殊用途,? ?用来完成特定的系统功能!
我们可以通过man getenv查看这个系统调用接口来获取环境变量
我们编写如下代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
printf("PATH:%s\n",getenv("PATH"));
return 0;
}
运行结果:
当然我们也可以把PATH改成其他想要获取的环境变量名称:
比如说改成USER
运行结果:
其实呢,系统会自动的给我们创建一张环境变量表的东西:
下面我们写一段这样的程序来打印我们的环境变量:
运行之后:
发现我们运行的结果与我们前面用env命令获取的结果基本上是一样的。
我们命令行启动的进程都是shell/bash的子进程,子进程的命令行参数和环境变量,是父进程bash给我们传递的!
子进程的环境变量是父进程传递的,那么父进程的环境变量又是从哪里来的呢?
我们前面说到更改环境变量问题,其实我们直接更改的是bash 进程内部的环境变量信息!而我们用户的每一次登录,都会给我们形成的新的bash解释器,并且新的bash 解释器会自动从某个配置文件自己的环境变量 表信息!
这里所说的配置文件就是
我们家目录下的一个隐藏文件 .bash_profile
我们可以用vim打开这个文件:
所以呢我们就可以知道环境变量信息是以脚本配置文件的形式存在的!
每一次登录的时候,我们的bash进程都会读取.bash_profile配置文件中的内容,为我们的bash进程形成一张环境变量表信息!
下面我们通过自己定义一些环境变量,例如:
我们定义一个环境变量名为:MYENV_109? ?值为:hellolinux
当我们echo $MYENV_109的时候是有内容的,可是当我们用env命令查看该环境变量是否能够添加到环境变量表当中运行之后发现并没有添加,我们把这样定义的环境变量叫做本地变量。
当我们打印的时候环境变量是有值的,而当我们用我们上面写的代码形成的可执行程序来运行获取环境变量时发现获取不到。
那么如何把我们自己定义的该环境变量导入到环境变量表当中呢?
我们通过export+环境变量名将我们的环境变量导入到环境变量表当中,那么我们的env命令和我们的可执行程序就都能获取该变量的了。
虽然这样可行了,但是当我们把xshell退出之后重新登录之后,获取我们刚刚导入的环境变量发现又不见了,原因就在于我们刚刚导入的环境变量仅仅只是在当前bash进程中修改的,根据我们上面的结论,bash进程在我们重新登录之后又会重新在我们的配置文件中取读取我们的环境变量,所以我们先前添加的环境变量会失效。
当我们重新登录后发现果然这个环境变量不见了,那么我们应该如何永久的导入一个环境变量呢?
那当然是通过在配置文件.bash_profile文件中添加shell脚本了,我们可以通过编辑该配置文件为如下:
修改前:
修改后:
我们添加了上述代码,然后保存退出,然后呢要想让该环境变量生效我们还需要重新登录让bash再重新读取一次该配置文件,然后登录成功之后:
我们查看一下这个配置文件的内容:
在执行上述语句发现已经生效了。
我们上面谈到,子进程的环境变量是由父进程进行传递的,那么我们父进程的环境变量就会一直被传递下去,所以以该父进程为根节点,所有由该父进程创建的子进程,再由子进程继续创建子进程的子进程都能够共享到该父进程的环境变量,所以我们说系统环境变量具有全局属性!!!
除了上述获取环境变量的方式其实还有一种方式可以获取到环境变量,我们c语言给我们提供了一个全局变量名字叫?environ
下面我们可以通过修改我们之前的代码实现:
运行结果:
可以发现我们刚刚添加再配置文件中的环境变量也被获取了。
所以我们就知道三种获取环境变量的方式了:
1. env命令
2.main函数传参
3.C语言提供的全局变量:environ
我们上面又谈到过一个本地变量,那么本地变量和我们的全局变量有什么区别呢?
本地变量只在bash进程内部有效,不会被子进程继承下去。
环境变量通过让所有的子进程继承的方式,实现自身的全局性!
我们发现当我们创建本地变量的时候,我们可以通过echo命令将本地变量的值给打印出来,但是我们的命令也是进程啊,按照这样的逻辑来说的话,当我们执行echo命令的时候是通过bash来创建子进程运行的,但是我们上面又说本地变量只在bash进程内有效,不会被子进程继承下去,那你这不是在跟我扯嘛?
其实啊,这里要跟大家说一下,我们的Linux中命令是由分类的,诸如我们上面用到的echo命令,export命令都是内建命令,而需要创建子进程的命令是我们的常规命令:
Linux命令分类:
1.常规命令:shell fork让子进程执行的命令
2.内建命令,shell命令行的一个函数,不会创建子进程,当然可以直接读取shell的本地变量喽。
1. echo: 显示某个环境变量值
2. export: 设置一个新的环境变量
3. env: 显示所有环境变量
4. unset: 清除环境变量
5. set: 显示本地定义的shell变量和环境变量
每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以’\0’结尾的环境字符串。