如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
PATH : 指定命令的搜索路径
HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)SHELL : 当前Shell,它的值通常是/bin/bash。
echo $name(name:环境变量名称)
env查看系统中所有的环境变量
一个程序要运行必须先加载到内存。因为CPU只能在内存中读取代码和数据。一个程序或指令必须让操作系统找到才能运行,操作系统需要按照一定路径去找。
PATH就是操作系统启动命令行解释器shell时,导入到shell上下文中,当我们执行指令时,我们让操作系统通过PATH中的路径搜索文件。
为了适应不同场景,要求操作系统启动bash,给我们做命令行解释,就必须预先设置好,未来我们要用到的变量,这批变量就叫环境变量。这些变量在bash后一直能被进程访问到。
环境变量本质是一个数据,一个字符串。
su -相当于让root用户重新登录,会重新加载root的很多东西,把root相关的上下文,环境变量全部加载进来,su就是把身份切换一下,对应的路径是没变的。
想通过指令方式获取环境变量,可以用env(打印全部)。
- 通过getnev获取环境变量的内容
USER环境变量最大的意义,可以标识当前使用Linux的用户。
一个文件如何知道用户有没有权限访问,用的就是USER环境变量。
root用户进入默认在/root路径下
echo能查到这个变量。
env查不到mycal环境变量,所以只能称mycal为shell本地变量,可以理解为C语言中的局部变量,而env能查到的环境变量是全局变量。
getenv是在全局环境变量获取一个环境变量内容的。
char*返回值的函数,获取失败就是NULL,成功就是获取到的内容。
mycmd.c
此时程序不能找到myval环境变量,是因为此时mycal还是本地变量,我们需要把它变成全局环境变量,才能用本程序mycmd的getenv访问到它的内容。
export可以对环境变量进行定义,并且一个本地变量已经存在, 想把它导成全局环境变量,只需要export NAME。
此时全局环境变量里就有mycal了。
mycmd程序也能打印出此环境变量了。
bash是一个系统进程,mycmd运行后也变成了一个进程,是bash的子进程,(实际是操作系统通过fork做的)。环境变量具有全局属性,本质是因为会被子进程继承下去。为什么要继承下去,是为了不同的应用场景,比如让bash帮我找指令路径和身份认证。子进程要根据环境变量实现很多功能。而之前说的本地环境变量,只会在bash进程内有效,不会被子进程继承。
set命令可以打印本地变量和全局变量,下面的图中yoursal不是全局环境变量,env不能找到,而set却能找到。
set命令中的这些,规定了命令行打印的格式。
unset用来取消环境变量。
也可以在export时直接定义环境变量。(your,上也可以不加引号,但建议加)
我们在使用ls命令时,其实是在创建子进程,ls也是个程序,而环境变量PWD可以被ls子进程继承下去,pwd环境变量被ls拿到,ls就知道当前在哪个路径下,所以显示文件时,我们不用带路径ls就知道文件在哪里。
自己实现pwd命令。
下面将mycmd拷贝到/user/bin路径下(文件存在这里就变成了指令),就可以直接用mycmd指令了,就不用./mycmd了。此时mycmd相当于pwd命令了。
简单来说,main函数是操作系统调用的,main函数的命令行参数是父进程传的
运行此程序。
命令行输入的东西变多时,./mycmd运行后argv不断变多。
所谓的命令行参数,是要把程序名和命令行参数传递给argv的。
一共有几个这样的命令,argv就要开辟多大,argc传的值就是多少。
我们输入时相当于输入一个长字符串,当main做命令行解析时,以空格为单位,进行拆分。然后从前往后放入argv这个指针数组中。
上述这些有什么用呢?
我们可以让一个程序,通过输入不同的指令,实现不同功能。
#include
2 #include
3 #include
4 #define MYPWD "PWD"
5 int main(int argc,char*argv[])
6 {
7 if(argc!=2)
8 {
9 printf("./mycmd[-a/-b/-c/-ab/-ac/-bc]\n");
10 }
11 if(strcmp(argv[1],"-a")==0)
12 {
13 printf("功能1\n");
14 }
15 if(strcmp(argv[1],"-b")==0)
16 {
17 printf("功能2\n");
18 }
19 if(strcmp(argv[1],"-c")==0)
20 {
21 printf("功能3\n");
22 }
23
24 if(strcmp(argv[1],"-ab")==0)
25 {
26 printf("功能4\n");
27 }
28
29 if(strcmp(argv[1],"-ac")==0)
30 {
31 printf("功能5\n");
32 }
33
34 if(strcmp(argv[1],"-bc")==0)
35 {
36 printf("功能6\n");
37 }
38
39 return 0;
40 }
和ls不同指令实现不同功能一个道理。 是通过命令行选项控制的。
Windows系统也有命令行选项。
比如关机命令。
argv是命令行参数表 ,env是环境变量表。 环境变量就像argv一样,通过父进程把环境变量传给和argv相似的命令行参数env,让环境变量在子进程得到继承。
没有表示env数量的变量,通过env最后一个字符是NULL(0)这个性质可以计数。每个进程都会有一个环境表,环境表是一个字符指针数组,每个指针指向一个以'\0'结尾的环境字符串,env就是这样的,通过env[i]判断结尾。
上面这个可以打印所有的环境变量。
子进程继承环境变量是因为父进程自动把环境变量传给了命令行参数env,然后子进程就能自动调用环境变量了。
- 我们就可以用environ获得环境变量,environ是个二级指针,它里面存的是env里面的值(即一级字符指针)。每个进程都会有一个环境表,环境表是一个字符指针数组,每个指针指向一个以'\0'结尾的环境字符串,environ就是这样的,通过environ[i]判断结尾。
- environ是c动态库里面定义的变量。和main函数的参数没有关系,environ里面的数据是进程创建的时候,c库复制进去的,它存的内容是char *env[]里面指针的地址。
- char *env[]在进程创建时就会形成。它是个全局的二级指针。
- main函数里面的第三个参数char *env[]里面数据就是通过environ传进去的。
1 #include
2 #include 3 #include 4 #include 5 int main() 6 { 7 extern char**environ; 8 int i=0; 9 for(i=0;environ[i];i++) 10 { 11 printf("%d:%s\n",i,environ[i]); 12 } 13 return 0; 14 }
在进程上下文中,获取环境变量的三种方式。
推荐第一种方式。
我们通过环境变量和stat获取文件属性的命令可以判断这个用户有没有对应的权限,没有权限就是Pression defined。
putenv:更改或添加一个环境变量。int putenv(char*string)(暂不用考虑)。
链接时如何找到对应的库,也是靠环境变量。
$HOME环境变量确定了用户刚进入账户时,pwd是/home/用户名,进入root用户时是/root。
指针类型的参数,数组传参要降维,降维成指向其内部元素类型的指针,char*env就要降维成char**,所以下面图中,第三个形参传的就是environ的内容。
每个进程都会有一个环境表,环境表是一个字符指针数组,每个指针指向一个以'\0'结尾的环境字符串。