【linux】linux进程概念(四)(环境变量)超详细版
小编个人主页详情 int cnt =5; while(cnt--) { printf("hello %d\n",cnt); } return 0; } printf("PATH:%s\n",getenv("PATH")); return 0; } printf("PATH:%s\n",getenv("USER")); return 0; } char who[32]; strcpy(who,getenv("USER")); if(strcmp(who,"root")==0) { printf("你是root用户,不受权限限制\n"); } else { printf("你是普通用户,受权限限制\n"); } return 0; } } if(arvc!=2) { printf("请输入%s -[a|b|c]\n",argv[0]); return 0; } else if(strcmp(argv[1],"-a") == 0) { printf("%s[1]-%s ",argv[0],argv[1]); printf("功能1\n"); } else if(strcmp(argv[1],"-b") == 0) { printf("%s[1]-%s ",argv[0],argv[1]); printf("功能2\n"); } else if(strcmp(argv[1],"-c") == 0) { printf("%s[1]-%s ",argv[0],argv[1]); printf("功能3\n"); } else { printf("default输入\n"); } return 0; }
运行结果如下
打印命令行参数表
- 对应的我们将argv这个指针数组叫做向量表,即命令行参数表,其中会默认在表的最后放入一个NULL空,即如果有四个参数,那么表中实际存储五个值,即参数1,参数2,参数3,参数4,NULL空,那么对应表的数组下标就为0到4,这个4代表有4个参数,所以我们就可以使用类似argv+[]的形式去遍历这个命令行参数表,并且可以使用argv[cnt]的形式作为for循环中的判断条件,以四个参数为例,那么表中第五个位置就会放置一个NULL空,那么对于这个NULL空实际上就对应0,所以随着变量cnt从0开始逐渐增加到4,那么当为4的时候对应命令行参数表,即指针数组的位置就为NULL空,那么就为0,此时结束for循环,那么就可以使用这种方式去遍历命令行参数表
#include #include #include int main(int argc,char* argv[]) { int cnt=0; for(;argv[cnt];cnt++) { printf("%s[%d]->%s\n",argv[0],cnt,argv[cnt]); } return 0; }
运行结果如下
命令行参数表,其底层如图所示
使用命令行第三个参数获取环境变量,打印环境变量表
- 其实main函数还可以有第三个参数,这个参数同样是指针数组类型,也是向量表,这里即环境变量表,那么我们就可以类似于打印命令行参数表的方式进行打印这里的环境变量表
#include #include #include int main(int argc,char* argv[],char* env[]) { int cnt=0; for(;env[cnt];cnt++) { printf("[%d]:%s\n",cnt,env[cnt]); } return 0; }
- 运行结果如下,我们的环境变量表就被打印出来了
环境变量表,其底层如图所示
七、环境变量为什么具有全局属性
理论
我们所运行的进程都是子进程,bash本身在启动的时候,会从操作系统的配置文件中读取环境变量信息,子进程会继承父进程交给子进程的环境变量,那么bash是所有进程的父进程,bash创建的子进程有可能再创建子进程,那么此时的进程关系就形成了一颗多叉树的形状,那么对应由于子进程要继承父进程的环境变量,而bash又是所有进程的父进程,那么bash中的环境变量在全部进程中都会有一份,那么此时环境变量就具有全局属性
如何证明?
在证明前小编需要先引出本地变量,即在bash命令行中定义出的变量是本地变量,本地变量只在本bash内部有效,不会被子进程继承,本地变量不同于环境变量,本地变量不归属环境变量,同样环境变量不归属本地变量
- export设置一个新的环境变量,unset清除环境变量
- 本地变量可以通过export将自己变为环境变量
- 当本地变量使用export变成环境变量之后,可以通过unset将环境变量再变回本地变量
- 可以使用set查看环境变量和本地变量
- 例如小编在bash命令行中定义一个本地变量MY_VALUE,接着再使用env显示环境变量,通过管道将数据传输给grep进行过滤MY_VALUE,结果什么都不显示,即无法在环境中找到MY_VALUE
- 那么接下来小编使用echo $查看MY_VALUE,查看到MY_VALUE对应的值了555
- 同样的我们还可以使用set去查看这个MY_VALUE本地变量,内容有点多这里小编只截取带有MY_VALUE的部分,可以看出其正好是小编定义的本地变量555
- 同样的也可以使用set显示环境变量和本地变量,使用管道将数据传输给grep,使用grep过滤本地变量MY_VALUE,那么就可以过滤出本地变量MY_VALUE
- 我们知道我们运行的程序,也是fork的子进程,继承fork的环境变量,那么小编使用如下代码通过getenv查找我们定义的本地变量MY_VALUE是否也被继承下来了
#include #include #include int main(int argc,char* argv[],char* env[]) { printf("%s\n",getenv("MY_VALUE")); return 0; }
运行结果如下
- 运行程序,访问出现错误,子进程中的环境变量中并没有MY_VALUE,是由于无法我们定义的本地变量MY_VALUE由于不是环境变量所以没有被继承
- 那么我们使用export将bash中的小编定义的本地变量MY_VALUE变成环境变量,这时候就可以使用env | grep MY_VALUE的方式在bash的环境变量中找到MY_VALUE,即此时MY_VALUE是bash的环境变量,那么此时我们运行我们的程序,如果程序它可以找到环境变量MY_VALUE,那么就说明子进程继承了fork的环境变量
- 看!找到了,那么就说明小编运行的自己的程序mycmd即子进程继承了fork的环境变量,所以所有的进程都会直接或间接的继承fork的环境变量,进程是一个多叉树的结构,那么在全局中,bash及其对应的子进程以及子进程的子进程等都会有一份和fork一样的环境变量,那么就说明环境变量具有全局属性
- 小编使用export(设置一个新的环境变量)将MY_VALUE直接设置环境变量之后,子进程会继承fork父进程的环境变量MY_VALUE,这时候运行程序,子进程可以找到环境变量MY_VALUE,接下来使用unset(清除环境变量)将环境变量变成本地变量,子进程不会继承fork父进程的本地变量MY_VALUE这时候运行程序,子进程就找不到环境变量MY_VALUE了,和第8点的结论一样,同样可以说明环境变量具有全局属性
八、命令分为常规命令和内建命令
内建命令的引出
- 观察上图小编在bash命令行中定义了一个本地变量MY_VALUE,本地变量MY_VALUE不会被子进程继承,那么根据小编之前的理论,指令都是fork的子进程,那么echo也是fork的子进程咯?那么就是说echo这个子进程不会继承本地变量MY_VALUE,那么为什么echo却可以查看本地变量MY_VALUE的值呢?
- 究其原因是因为之前小编之前为了大家理解不得不讲bash是通过fork创建子进程去完成任务的,其实事实不是这样的,而是在bash中有两批命令,第一批是常规命令,即bash通过fork创建子进程完成任务,第二批是内建命令,即bash不使用fork创建子进程而是由bash亲自执行任务,类似于bash调用了自己写的或系统提供的函数
模拟内建命令
系统调用接口chdir,可以将当前调用的进程的工作目录更改至指定路径下
那么由于查看进程的工作目录(当前进程的路径即当前进程所在的目录位置)需要找到其PID和进入对应的proc中进程对应的PID中的cwd需要一定时间,所以小编需要在程序运行起来之后对进程进行休眠,即使用系统调用接口sleep,sleep可以指定让进程休眠n秒
使用系统调用接口chdir和sleep时不要忘记包头文件 #inclue
- 那么小编就是用命令行参数和chdir模拟cd命令 cd / 即改变当前路径至根目录
#include #include int main(int argc,char* argv[]) { sleep(30);//这里需要给小编查看进程未改变前的工作目录留出时间 if(argc==2) { chdir(argv[1]); } sleep(30);//这里进程已经更改完成工作目录,但是此时进程不能退出结束,一旦退出结束之后 //小编将无法查看更改后的工作目录了,所以这里需要留出时间给小编用于查看进程 //更改之后的工作目录 return 0; }
运行结果如下,更改成功,将进程的工作目录更改至根目录/了
- 类似的bash中的cd指令也是这样的道理,只不过大概是这种形式,将小编的程序名mycmd替换成cd,bash也是一个进程,bash内部也会由很多语句函数,在bash内部中直接使用strcmp去判断这个argv[0],第一个参数位置是不是cd,如果是cd,那么就去在bash内部去调用chdir系统调用接口将当前bash的工作目录更改到第二个参数(要更改的工作目录)argv[1]上,因为调用cd是这种形式,例如 调用 cd / 这个 cd / 是一个字符串那么就是bash的命令行参数,会将 cd / 通过空格进行划分,cd作为第一个参数,将其字符串的指针放到argv[0]这对应就为要调用的指令,/ 作为第二个参数,将其字符串的指针放到argv[1]这对应就为要更改的工作目录位置,那么这就是cd指令的原理,这种的在bash内部调用bash自己实现的函数或系统实现的函数的命令成为内建命令
- 那么echo也是一个内建命令,那么内建命令echo不是bash的子进程,而是在bash内部实现的命令,那么就可以使用bash的本地变量进行显示了,这样小编在bash命令行中定义的本地变量MY_VALUE就可以自然而然的被bash的内建命令echo进行调用显示其对应值999了
#include #include #include int main(int argc,char* argv[]) { if(strcmp(argv[0],"cd")==0) { chdir(argv[1]); } return 0; }
九、通过第三方变量environ获取环境变量
我们还可以通过第三方变量environ获取环境变量,libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时,要进行声明
- 在使用environ的时候的函数原型应该是int main(int argc,char* argv[])的形式
- 同时还应该声明二级指针environ
- 那么在使用的时候类似的就要类似于原main中第三个参数env去使用env[]去访问的形式,这里的environ也应该使用environ[]的形式进行去访问环境变量
- 那么小编使用如下代码使用第三方变量environ去访问获取并且遍历环境变量
#include #include int main(int argc,char* argv[]) { extern char** environ; int cnt=0; for(;environ[cnt];cnt++) { printf("[%d]->%s\n",cnt,environ[cnt]); } return 0; }
- 运行结果如下,遍历成功
总结
以上就是今天的博客内容啦,希望对读者朋友们有帮助
水滴石穿,坚持就是胜利,读者朋友们可以点个关注
点赞收藏加关注,找到小编不迷路!