基本概念
-
环境变量(environmentvariables)⼀般是指在操作系统中⽤来指定操作系统运⾏环境的⼀些参数
-
如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪⾥,但是照样可以链接成功,⽣成可执⾏程序,原因就是有相关环境变量帮助编译器进⾏查找。
-
环境变量通常具有某些特殊⽤途,还有在系统当中通常具有全局特性
举例理解
在编写c/c++代码时,main函数我们称为"我们自己的程序入口",但main函数其实也是被其他函数调用的,在linux系统下是被一个叫statr()的函数调用。同时,main函数也是有参数的。
-
argv[]:是一个指针数组,指向传入的字符串
-
agrc:表示子字符串个数
int main(int argc,char* argv[])
{
//***
return 0;
}
将代码改写成以下形式放到linux下运行,得到以下结果
#include<stdio.h>
int main(int argc,char*argv[])
{
for(int i=0;i<argc;i++)
{
printf("argv[%d]:%s\n",i,argv[i]);
}
return 0;
}


这与我们在linux下输入的指令类似,如:
-
ls -a
-
ls -l
原因
这是因为我们输入的./code -a -b -c会先被bash获得,然后被拆分成一个一个的字符串,将他保存到argv[0...i]中去。在Linux中,我们输入的命令如"ls -a"会先被bash获得,然后被bash拆分成一个一个的字符串。这样,我们就能通过命令行参数实现一个个子功能了,就好比"ls -a","ls -l"这般
main的命令行参数,是实现程序不同子功能的方法:也是指令选项实现的原理
#include<stdio.h>
2 #include<string.h>
3 int main(int argc,char*argv[])
4 {
5 if(argc!=2)
6 {
7 printf("Usage:./code [-a|-b|-c]\n");
8 return 1;
9 }
10 const char*arg=argv[1];
11 if(strcmp(arg,"-a")==0)
12 {
13 printf("这是功能1\n");
14 }
15 else if(strcmp(arg,"-b")==0)
16 {
17 printf("这是功能2\n");
18 }
19 else if(strcmp(arg,"-c")==0)
20 {
21 printf("这是功能3\n");
22 }
23 else
24 {
25 printf("Usage:./code [-a|-b|-c]\n");
26 }
27 // for(int i=0;i<argc;i++)
28 // {
29 // printf("argv[%d]:%s\n",i,argv[i]);
30 // }
31 return 0;
32 }

由此我们可以得出,进程是有一张表的,argv表,用来支持实现选项功能
执行一个命令先需要先找到它。
我们使用的指令其实就是二进制程序,与上面所实现的"code"其实没有区别。
那为什么使用指令时可以不用带路径?
-
系统中存在环境变量,来帮助系统找到对应的指令,即二进制文件
-
若把上面我们实现的"code"拷贝到usr/bin/路径下,就不用带路劲了
为什么将二进制文件放到usr/bin/路径下系统就能找到呢?
- 因为系统中存在环境变量(PATH),环境变量会标识一段路径,环境变量告诉系统到哪些路径下查找二进制文件
查看环境变量:env
-
环境变量的构成:名称=内容
-
标识环境变量的唯一性是用名称来标识的

- 表示环境变量的唯一性是靠名称
获得环境变量的内容
- echo $PATH

shell查找指令就是从环境变量的路径中去查找。以":"为分隔符,将路径一个一个取下来,然后添加到指令前,若存在该指令则执行,否则就报错command not found。
比如查找"ls"指令:
//将路径取下来添加到指令前
/usr/local/bin/ls//若没找到,则继续向下一个路径中查找
/usr/bin/ls//若找到,则执行指令
添加环境变量:
//第一种
PTAH=$PATH:+要添加的环境变量内容
//这种方式添加环境变量,不会覆盖原来环境变量的内容

//第二种
PATH=要添加的环境变量内容
//这种方法会覆盖原有内容

想要恢复初始的PATH内容,直接重启服务器就行
如何理解环境变量?
存储角度 :在用户登录的时候,会分配一个bash,bash会在自身内部形成一张表 ,即"环境变量表",当用户输入命令时,bash会先得到这串命令,然后分割形成一张命令行参数表 ,再拿着命令(如ls)在环境变量表中找到PATH,将PATH中保存的路径一 一添加到命令前查找命令,找到就执行,没找到就告诉系统"command not found"。所以在bash内部会有两张表:命令行参数表,环境变量表(也就是一个指针数组char*[])。
环境变量表
环境变量表就类似c++中学的键值对。
在bash启动的时候,它会为环境变量表new、malloc一段空间,同时还会new、malloc一段空间用来存放环境变量内容组成的常字符串。
所以环境变量到底是什么?
就是k、value的常字符串


所以环境变量是保存在bash进程自己的上下文中
环境变量最开始是从哪来的呢?
- 系统相关的配置文件
系统给每个用户的家目录下存放了两个隐藏文件,如下。
也就是说,在用户登录的时候,bash会读取以下文件,然后给用户配置环境变量

所以环境变量表的本质:是操作系统在自己配置文件中提供的。
所以它叫做:系统中,用来指定系统运行环境的一些参数,而这些参数未来是被bash使用的,bash使用了,说明用户间接的使用了
所以,执行命令要先找到它,找的任务是由bash自己执行的。
比如:"cd ~"为什么能返回到家目录?
因为bash的环境变量表中存放了一个环境变量叫"HOME",在用户登录的时候bash会把用户当前的路径保存HOME中。也就是HOME=/home/用户名。将cd命令在环境变量表中查找,找到后,再将参数"~"也传过去,bash接收到命令后,便开始执行命令。所以cd ~能返回家目录
认识更多环境变量
记录历史命令的环境变量
最多记录一千条历史命令

记录当前主机的名的环境变量

记录当前工作路径的环境变量

记录上一次路径

记录当前登录的用户

获取环境变量的方法
操作
-
export:导入环境变量
-
env:查看环境变量
-
echo $XXX:查看单个环境变量
-
unset:取消环境变量
代码方式
关于main函数
main函数也是被其他函数调用的,linux下是_start(),在代码编译的时候编译器会扫描main函数带了几个参数,然后根据参数量进行调用main函数

方法一:
//获取的是父进程的环境变量
//同时,父进程的环境变量也可以被孙子进程、曾孙..继承
//说明环境变量是一个具有全局属性变量
export MYPATHY1=111
export MYPATHY2=222
export MYPATHY3=333
#include<stdio.h>
#include<string.h>
int main(int argc,char*argv[],char*env[])
{
(void)argc;
(void)argv;
for(int i=0;env[i];i++)
{
printf("env[%d]->%s\n",i,env[i]); }
方法二:
//getenv根据环境变量名查找环境变量,返回环境变量的指针
#include<stdio.h>
#include<stdlib.h>//getenv函数的头文件
#include<string.h>
int main(int argc,char*argv[],char*env[])
{
(void)argc;
(void)argv;
(void)env;
char*path=getenv("PATH");
if(path==NULL)return 1; printf("PATH->%s\n",path);
return 0;
}
--------------------------------------------
//写一个只有自己能执行的程序
#include<stdio.h>
#include<string.h>
int main(int argc,char*argv[],char*env[])
{
(void)argc;
(void)argv;
(void) env;
char*who=getenv("USER");
if(strcmp(who,"xiaohao")!=0)
{
printf("Only xiaohao\n");
return 1;
}
printf("这是正常的执行逻辑\n");
return 0;
}
/*
*/
为什么要让父进程的子进程、孙进程....继承自己的环境变量
子进程继承了环境变量,就能执行一些个性化操作,比如定制一个只能自己执行的程序
方法三:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>//environ的头文件
extern char**environ;//使用前需要先声明
int main(int argc,char*argv[])
{
(void)argc; (void)argv;
for(int i=0;environ[i];i++)
{
printf("env[%d]->%s\n",i,environ[i]);
}
return 0;
}
//char**environ是指向环境变量表的一个二级指针
环境变量的特性
- 环境变量具有全局特性
补充两个概念
-
bash允许用户定义本地变量,且不会被子进程继承,只允许在bash内部使用
//设置本地变量 [xiaohao@iZ2zeebi0w2dzgbbqlrmxgZ 12-31]$ i=10 //查看本地变量 [xiaohao@iZ2zeebi0w2dzgbbqlrmxgZ 12-31]$ set //查看单个本地变量 [xiaohao@iZ2zeebi0w2dzgbbqlrmxgZ 12-31]$ echo $i //取消本地变量 [xiaohao@iZ2zeebi0w2dzgbbqlrmxgZ 12-31]$ unset
环境变量是在谁里面?
- bash
- 既然在bash里面,为什么export导环境变量能导到bash里?命令执行后不都是bash的子进程吗?
- 因为执行export不需要创建子进程,而是bash自己执行。bash自己调用函数或者系统调用完成。export是内建命令----built-in command