环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数。
比如说:
我们可以使用echo命令来查看环境变量,方法如下:
echo $NAME //NAME:你的环境变量名称
下面我们使用echo命令来试一下吧
查看PATH环境变量:
[root@izuf65cq8kmghsipojlfvpz ~]# echo $PATH
我们之前说过在Linux下一切皆文件,因此Linux下的这些命令、程序、工具本质都是一个可以执行的文件。
那么问题来了:
为什么我们这里执行我们自己生成的proc这个可执行文件的时候必须在前面要加上./才能够跑,但是执行ls、pwd这些系统指令的时候不带./却可以跑呢?
这个问题其实很好回答:
我们想要执行一个可执行程序,那么我们第一步就必须得找到它在哪里,然后再去执行它。因为系统不知道proc这个文件在哪,加上./是为了帮助系统找到proc这个可执行程序的。
那么问题又来了:
既然你说要执行一个可执行程序必须得先找到它,我们这里加上./是为了帮助系统找到proc,那么为何系统的命令不用带路径呢?
其实系统的命令也带了路径的,系统它是通过环境变量PATH去找到pwd、ls这些系统命令的,我们通过echo命令来查看一下环境变量PATH一起看一下吧:
我们可以看到环境变量PATH中有多条路径,这些路径以冒号作为分隔符隔开,当我们使用pwd或者ls命令的时候,系统就会依次在环境变量PATH的这些路径中进行查找。
如果某条系统命令在这些路径中找到了就会停止查找,然后执行这条命令。
我们可以通过which指令查看一下ls命令是在哪里的:
可以看到ls命令其实是在环境变量PATH的其中一条路径下的。
明白了上面的那些问题之后,我现在又有了一个小问题,那既然系统指令可以不用加./就可以执行,那我们自己生成的可执行程序可不可以这样呢?
答案是可以的,下面我教大家两种方法。
方法一:将你自己生成的可执行程序拷贝到环境变量PATH的某一路径下。
我们知道在未指定路径的情况下操作系统会根据环境变量PATH里面的路径去查找,那么我们可以将我们可执行程序拷贝到PATH里面的其中一条路径下,后面我们执行该可执行程序的时候就不用再带./了
[root@izuf65cq8kmghsipojlfvpz ~]# cp proc /usr/bin
方法二:将可执行程序所在的目录导入到环境变量PATH中
[root@izuf65cq8kmghsipojlfvpz ~]# export PATH=$PATH:/root
先给大家看一个错误示范:
我们将可执行程序所在目录导入环境变量PATH的时候,最好不要这样去导入,这样去导入的话覆盖掉环境变量PATH里面的路径,导致你执行不了系统指令,但是遇到这种情况也不要慌张。因为我们并不是改了环境变量的配置,因此我们只需要重新启动一下Xshell,环境变量就会恢复到原来的样子。
正确做法:
我们将可执行程序的所在目录导入到环境变量PATH后,我们再去执行这个可执行程序的时候就可以像执行系统命令那样直接去执行而不用带./了。
在之前讲Linux指令的时候,我们说过cd~命令可以进入到当前用户的工作目录,并且每个用户的工作目录是不一样的。那么问题来了操作系统它是怎么知道每个用户的工作目录是在哪的呢?
因为每个用户的环境变量HOME是不同的,操作系统通过环境变量HOME来保存该用户的工作目录
普通用户:
超级用户:
我们平常使用的时候可能会用到各种Shell,我们可以通过查看环境变量SHELL来知道当前所用的命令行解释器的种类。
我们上面说了一些环境变量,那么问题来了:
我们应该如何去理解环境变量呢?
在语言上面定义变量的本质是在内存中开辟空间(有名字),我们不要去质疑OS开辟空间的能力!!!
环境变量的本质是OS在内存/磁盘文件中开辟的空间,用来保存系统相关的数据,环境变量是具有全局属性的。
系统上还存在着一种变量,它是与本次登录(session)有关的变量,只在本次登录有效(本地变量)
在操作系统当中,环境变量的组织方式是这样的:
系统下的每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以’\0’结尾的环境字符串。
你觉得main函数可以带参数吗?如果可以带参数的话那能带几个呢?
我想大部分人可能都会觉得main是可以带参数的,并且只可以带一个,因为大家可能只见过这个:main(void)
其实我们的main函数是可以带参数,并且它是有三个参数的,因为我们平时基本上都不怎么用他们,所以我们并没有将这三个参数给写出来。
main函数是有这三个参数的:
main(argc,argv,envp);
main函数的前两个参数我们通常称之为命令行参数
我们先来看一下main函数的前两个参数
我们在Linux系统下,编写以下代码,生成可执行程序并执行它。
执行结果:
看到了执行结果之后我们来讲一下前两个参数吧
第二个参数argv我们上面说过它是一个字符指针数组,我们将可执行程序以及它后面的选项都看作是字符串,argv的第一个字符指针存放的是可执行程序,后面几个字符指针存放的是可执行程序后面的选项,最后一个字符指针存放的是NULL。
第一个参数argc它决定了字符指针数组里面有几个有效的命令行字符串。
我们知道我们可以通过调用系统指令+不同的选项实现不同的子功能,那么这里我们同样可以通过main函数的几个参数来根据你所给的选项不同,从而打印不同的结果或者实现不同的功能。
下面我们来看一段代码:
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<string.h>
4
5 int main(int argc,char* argv[])
6 {
7 if(argc!=2)
8 {
9 printf("Usage: ./proc -[a|b]\n");
10 return 1;
11 }
12
13 if(strcmp(argv[1],"-a")==0)
14 {
15 printf("hello mlf\n");
16 }
17 else if(strcmp(argv[1],"-b")==0)
18 {
19 printf("hello nihao\n");
20 }
21 else
22 {
23 printf("hello world\n");
24 }
25
26 return 0;
27 }
运行结果:
总结:
指令有很多选项,用来完成同一个命令的不同子功能,选项的底层使用的就是我们的命令行参数!!!
main函数除了可以传递命令行参数它还可以传递env,main函数的第三个参数接收的就是我们上面说的环境变量表,我们可以通过main函数的第三个参数来获得系统的环境变量。
下面我们来看一段代码:
运行结果:
大家对于这个运行结果是不是有点眼熟呢?
没错,这里的运行结果其实就是输入env指令打印出来的各个环境变量的值。
我们这里除了使用main函数的第三个参数获取环境变量外,我们还可以通过第三方变量environ来获取
我们来运行一下可执行程序,查看一下运行结果:
注意: libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时 要用extern声明。
除了上面两种获取环境变量的方式外,我们还可以通过调用getenv函数来获取环境变量,这也是我们最常用的一种方式。
getenv函数可以根据所给的环境变量名,在环境变量表中进行搜索,从而找到对应的环境变量拿出后面的内容。
下面我们来看一段代码:
运行结果:
下面我们来看一段代码:
运行结果:
可以看到我们执行这个可执行程序之后,什么都没有打印,这是因为我们的MYString还只是一个本地变量,并不是一个环境变量,它只能够父进程自己去用。
下面我们让父进程bash通过export指令,将MYString变成环境变量再来看一下吧
可以看到再次运行刚刚的可执行程序后,会打印出我们想要的结果,这是为什么呢?
这是因为子进程继承了父进程bash的环境变量。
总结:环境变量具有全局属性,本质是环境变量可以被子进程继承下去。环境变量影响了整个用户系统。