1.什么是环境变量
环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数
- 比如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
- 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性
windows里也有环境变量
2.认识几个环境变量
2.1.PATH
- PATH : 指定命令的搜索路径
为什么我们使用指令时不用加./,而我们使用自己编译的可执行程序就需要加./?
这个是因为使用指令的时候,系统默认在/usr/bin/里去查找,那我们自己的可执行程序为什么没有这个待遇?
原因在于linux会给指令搜索提供一个环境变量PATH我们可以把它显示出来
查看环境变量指令: echo $NAME //NAME:你的环境变量名称
上图是我Linux用户下的PATH变量,这些路径都由:进行分隔。
当我们输入我平时的ls、pwd等等指令时直接就可以使用,就是因为他们都是在/user/bin目录下,操作系统可以自动寻找。
但是我们运行一个陌生路径下的可执行程序时就得声明为当前路径下,这就是因为我们在PATH环境变量中没有定义此路径。操作系统不能自动寻找。
我们可以将一个程序放到PATH有的路径下,或者将当前的绝对路径加入到PATH变量中去,我们就可以直接使用此指令。
我们可以往PATH添加路径
这样子这个目录下
面的所有可执行程序都能当成指令来运行了
实际上,我们刚刚修改的PATH环境变量是一种在内存中的环境变量,所以无需担心。
如果不小心改错了,只需关闭Xshell并重新登录即可。这个PATH环境变量是在shell中保存的。
然而,当shell尚未存在时,即在系统启动时,环境变量是从哪里来的呢?
实际上,这些环境变量预先存储在我们系统的一些配置文件中。当系统启动时,它们会被加载到内存中。因此,现在你不必担心你的环境变量被错误修改,只需重新启动一下,我们的环境就会恢复
2.2.HOME
- HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
在我们平是登录xshell的时候普通用户就会直接进入自己的家目录/home/xxx而root用户会进入自己家目录/root ,那么系统是怎么知道我们的工作目录的呢?
主要原因就是因为当我们在登录时,shell就会直接识别到当前登陆账户是谁,然后给当前用户填充$HOME环境变量,当我们登录时,此时默认就直接cd到了$HOME目录下。这就是我们每次登录之后都会处于自己对应的家目录的原因。
2.3.获取环境变量env&&getenv
在bash
中我们可以使用env命令来获取bash
从系统中继承下来的所有环境变量
我们可以通过下面这个来查询其他环境变量
也可以使用getenv来获取某一个环境变量。
#include <iostream>
#include <unistd.h>
using namespace std;
int main()
{
cout<<"PATH="<<getenv("PATH")<<endl;
return 0;
}
2.4环境变量USER
USER
环境变量用来标识当前登录的用户。在终端中输入命令echo $USER
,可以查看当前登录用户的用户名。这个环境变量是bash/shell
在启动时自动加载的,它记录了当前登录的用户。
我们再来写一段demo代码:
#include <iostream>
#include <unistd.h>
using namespace std;
int main()
{
cout<<"USER="<<getenv("USER")<<endl;
return 0;
}
了解了上面内容,我们就可以模拟一下系统权限对普通用户和root用户的判定方式:
#include <iostream>
#include <unistd.h>
#include <cstring>
using namespace std;
int main()
{
char who[32];
strcpy(who,getenv("USER"));
if(strcmp(who,"root")==0)
cout<<"root用户不受条件约束"<<endl;
else
cout<<"普通用户受权限约束"<<endl;
cout<<"USER:"<<getenv("USER")<<endl;
return 0;
}
因为有环境变量的存在,我们的系统就已经具备了能够认识你这个人是谁的能力,只要能认识你是谁,就可以和文件属性当中文件的拥有者所属组和文件所对的权限所对比,进而判定出你有没读写权限
3.命令行参数
我们C/C++中的main函数其实是可以传参的,这两个参数我们称之为命令行参数:
int main(int argc, char* argv[ ])
其中argv是一个指针数组,里面保存了字符串的地址,argc决定了argv中的元素个数,我们来尝试打印一下argv中的内容:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main(int argc, char* argv[])
{
for(int i = 0; i < argc; i++)
{
printf("argv[%d] == %s\n", i, argv[i]);
}
return 0;
}
我们在命令行输入几个数后,打印出来就有几个。
3.1.命令行参数的作用
那命令行参数有什么用呢?看代码:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
int main(int argc, char* argv[])
{
if (argc != 4)
{
printf("Usage: \n\t%s -[add|sub|mul|div] x y \n\n", argv[0]);
}
int x = atoi(argv[2]);
int y = atoi(argv[3]);
if (strcmp("-add", argv[1]) == 0)
{
printf("%d + %d = %d\n", x, y, x + y);
}
else if (strcmp("-sub", argv[1]) == 0)
{
printf("%d - %d = %d\n", x, y, x - y);
}
else if (strcmp("-mul", argv[1]) == 0)
{
printf("%d * %d = %d\n", x, y, x * y);
}
else
{
printf("%d / %d = %d\n", x, y, x / y);
}
return 0;
}
命令行参数可以通过输入不同的选项,来控制程序执行不同的功能代码。
命令行参数为指令,工具,软件等提供命令行选项的支持!
命令行参数是Linux指令的基础,从上面的程序我们可以联想出我们在Linux中使用的指令后面跟的就是命令行参数:
我们使用的ls -al 、ll -al等等都是命令行参数。
我们使用的环境变量不是一两个而是一堆,并且彼此之间没有关系,一般是系统内置的具有特殊用途的变量。而定义变量的本质就是开辟空间,操作系统就是在运行中开辟空间的。
3.2.main函数的第三个参数
main函数除了上面那两个参数,还有一个叫做char *env[]
的参数;
int main(int argc,char *argv[],char *env[])
{
return 0;
}
我们也可以来打印一下env
中的内容:
#include <iostream>
#include <unistd.h>
#include <cstring>
using namespace std;
int main(int argc,char *argv[],char* env[])
{
for(int i = 0;env[i];i++)
{
printf("env[%d]->%s\n",i,env[i]);
}
return 0;
}
还可以通过第三方变量environ获取
#include <stdio.h>
int main(int argc, char *argv[])
{
extern char **environ;
int i = 0;
for(; environ[i]; i++){
printf("env[%d]->%s\n",i,environ[i]);
}
return 0;
}
argv和env
的结构一模一样,所以我们的C/C++代码一共会有两张核心向量表,一张叫做命令行参数表,一张叫做环境变量表。环境变量表会从父进程中继承下来。
我们所运行的进程,都是bash的子进程, bash本身在启动的时候,会从操作系统的配置文件中读取环境变量信息,子进程会继承父进程交给我的环境变量!
3.3.验证环境变量的继承
我们可以自己定义一个环境变量导入系统中的环境变量表当中,我们想要增加一个环境变量可以使用下面命令:
export MY_VALUE=666666666666
可以看到我们自己定义的环境变量已经被导入到环境变量表当中。
我们接下来再去执行我们刚刚的代码:
看到env[17]了嘛!!!可以发现它里面也有 MY_VALUE 这个环境变量,说明子进程test继承了父进程 bash 的环境变量。
删除一个环境变量可以使用unset 环境变量
命令。
unset MY_VALUE
4..环境变量通常是具有全局属性的
环境变量通常具有全局属性,可以被子进程继承下去
#include <stdio.h>
#include <stdlib.h>
int main()
{
char * env = getenv("MYENV");
if(env){
printf("%s\n", env);
}
return 0;
}
直接查看,发现没有结果,说明该环境变量根本不存在
导出环境变量
export MYENV="hello world"
再次运行程序,发现结果有了!说明:环境变量是可以被子进程继承下去的!
5.常规命令&&内建命令
我们再来看这个图中,我们定义了本地变量MY_VALUE,我们的可执行程序(bash的子进程)都读不到这个变量,为什么echo
可以读取到呢
我们使用可执行程序myproc去读取我们的本地变量
其实我们命令行上执行的指令不一定都要创建子进程,就好比王婆说媒一样,一些有任务特别难的或者风险的,王婆就会找别人去帮忙说媒。如果是一些很有把握的事情,那王婆还是愿意自己去做的。
由此可以推出我们指令是有区别的:
- 常规命令:通过创建子进程完成的
- 内建命令:bash不创建子进程,而是有自己亲自执行,类似于bash调用了自己写,或者系统提供的函数。
所以echo是一个内建命令,它是由bash自己执行的,与此同时我们的cd命令也是一个内建命令,我们可以通过调用chdir来改变当前工作目录。
#include <iostream>
#include <unistd.h>
#include <cstring>
using namespace std;
int main(int argc,char *argv[])
{
sleep(30);
printf("change begin\n");
if(argc==2)
{
chdir(argv[1]);
}
printf("change end\n");
sleep(30);
return 0;
}
在命令行中输入 cd 命令的时候,bash 并不会创建子进程,而是去判断命令行参数是否为 cd,如果是就直接去调用 chdir 系统接口来切换bash的工作目录