1.环境变量概念
环境变量一般指的是操作系统运行环境的一些参数
比如说,我们C/C++编译完要链接各种库的话,尽管我们并不知道这些库的位置,我们依然可以完成库的链接,这就是环境变量给你完成的
2.一个例子,一个环境变量
开始之前,我们先理解一个东西
深得理解一下命令行参数
在我们的main函数其实是有两个参数的
一个是int argc,一个是char* argv[]
比如我们的code是编译完的可执行程序的话,我们./code就是执行这个命令,如果我们后面加了-a -b -c这样的选项的话,就能完成各种子功能
./code -a/-b/-c
比如上面这行命令,bash会把它分成两个字符串,形成一个表,传递给可执行函数的main函数里
我们可以写代码示范一下
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc,char* argv[])
{
for(int i = 0;i<argc;i++)
{
printf("argv=%s\n",argv[i]);
}
return 0;
}
证明完毕
我们再写一个代码,要求必须带-a 或者 -b -c的选项,如果没带就提示要带选项,如果带了就表明是功能几
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
int main(int argc,char* argv[])
{
if(argc !=2)
{
printf("Usage %s : [-a/-b/-c]\n",argv[0]);
return 1;
}
const char* arg = argv[1];
if(strcmp(arg,"-a")==0)
{
printf("这是功能1\n");
}
else if(strcmp(arg,"-b")==0)
{
printf("这是功能2\n");
}
else if(strcmp(arg,"-c")==0)
{
printf("这是功能3\n");
}
else
{
printf("Usage %s : [-a/-b/-c]",argv[0]);
}
return 0;
}
main主函数的两个参数,是实现程序不同子功能的方法
下面就来说环境变量,
我们说到,linux的命令都是C语言写的,都在/usr/bin里
我们的二进制程序一般都是在当前目录下./code这样执行,那我们常用的ls cat 之类的bash命令为什么就能不加路径直接执行呢?
这就是环境变量了,系统里有环境变量来帮助bash找到对应的命令的位置
如果我们把我们自己的程序也拷贝到/usr/bin里,那我们也不需要路径直接就能运行了
如图所示,果然如此
/usr/bin是在环境变量里的,这个环境变量就叫PATH 存放着系统中搜索指令的默认搜索路径
我们env可以查看所有环境变量
可以找到这就是PATH路径
我们也可以直接
echo $PATH
那我们直接把我们自己二进制程序的路径加到PATH里,是不是也可以直接执行我们的二进制程序了
PATH=你的二进制程序路径
但是这样的话
我们其他命令就没法执行了,坏了,这是因为啥呢
我们这种PATH=路径,是直接覆盖上去了
我们怎么恢复呢?我们可以找到之前的PATH路径,重新PATH=,覆盖回去
我们也可以重启bash,bash会自动从配置文件里重新加载环境变量
我们正确不覆盖的方法是
PATH=$PATH:要添加的路径
那,我们从存储的角度理解一下环境变量吧
我们启动bash的时候,bash会用一个表(char*数组)来存储每个环境变量和它的所有路径
同时当我们在bash输入命令的时候,也会形成一个命令行的表
当我们输入命令的时候,比如ls,我们的bash会从PATH所有路径里找这个叫ls的二进制程序,找到了就执行,找不到就command not found
环境变量最开始是从配置文件里来的
,bash_profile从.bashrc里找PATH,.bashrc从/etc/.bashrc里找PATH
如下图就是.bashrc
如果我们对PATH加路径的话,只是这一次,下次重新登录还会恢复到之前的,如果我们想永久更改PATH路径,就要改配置文件
我们在bash_profile这个配置文件里添加路径,下次bash再登录的时候就会把这个路径也加载上,就能找到我们该路径的二进制程序了
如果有十个用户登录,那就有十个bash进程,每个bash进程都会加载一份环境变量
我们windows上也有PATH环境变量
在.bash_profile里把要添加的路径加上去
然后
source ~/.bash_profile
然后就能执行该路径的可执行程序了,并且重新登录也不会变
3.认识更多的环境变量
1°HOME
该环境变量存储的就是当前用户的家目录
然后介绍俩命令
who是查看登录的用户是谁
whoami是查看当前使用终端的用户
我们cd ~ 这个~就是从HOME环境变量里找的家目录路径
2°SHELL
SHELL变量记录的是启动终端的默认SHELL路径
3°USER & LOGNAME
LOGNAME和USER大部分情况下都是一致的,登录的时候自动就会填上登录的用户名
假如我们用自己的用户登录,然后su转成超级用户的时候
只是切换了使用终端的用户,并没有重新登录,USER和LOGNAME是不变的
如图所示,而当我们su -的话就表示重新登录
退出登录的时候则会切换回去
3°HISTSIZE
该环境变量表示bash里存储的之前用过的命令最多的条数
我们上下键找历史命令就和它有关
我们bash里应该会把历史命令用队列存起来
4°HOSTNAME
标识主机的名字
5°PWD & OLDPWD
该环境变量是存储的是当前路径
OLDPWD是存储上一个路径
有了oldpwd,我们就可以快速回到上一个目录
这个命令叫
cd -
4.获取环境变量的方法
1°操作
我们先谈操作
我们前面已经学了
env
查看所有环境变量
echo $XXXX
查找某一个环境变量的内容
export MYENV=112233444
这个表示增加一个MYENV的环境变量
unset XXX
取消某个环境变量
ok操作我们学了,我们接下来再在代码里学一下怎么获取环境变量
首先,我们要知道 main函数有参数吗?如果有,最多有几个?
答案是有,最多有三个
分别是 int argc,char*argv,char* env
也就是说第三个参数就是bash的环境变量的表
我们试着用一下这个env(这三个参数都是父进程bash传给子进程的)
#include <stdio.h>
#include <stdio.h>
int main(int argc,char *argv[],char* env[])
{
for(int i = 0;env[i];i++)
{
printf("env[%d]-> %s\n",i,env[i]);
}
}
这样果然就能打印出我们的环境变量列表了
那我们的代码编译的时候,是怎么知道main函数的参数的呢
实际上啊,我们代码编译的时候,不是直接就调用main函数了的
而是操作系统会把一些变量加载到_start,_start是真正的入口
_start再进行调用main函数的操作
大概就是这样
_start
{
int ret = 0;
int arg_count = 0;
arg_count = 3;
if(arg_count == 0)
ret = main();
else if(arg_count == 1)
ret = main(argc);
else if(arg_count ==2)
ret = main(argc,argv[]);
else
ret = main(argc,argv[],env[]);
}
至此,我们第一个用代码获取环境变量的方法有了
即(1).main函数的env参数 ,即父进程bash的env列表
当然。父进程的环境变量是可以被子进程继承的,如果bash环境变量进行了修改,子进程的环境变量会和bash的环境变量列表一样
我们试一下
再执行我们程序
环境变量为什么能被继承呢?因为环境变量是有全局性的呀
那么接下来我们再学一个系统调用接口吧
它叫做getenv()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char* argv[],char *env[])
{
char *value = getenv("PATH");
if(value == NULL) return 1;
printf("PATH->%s\n",value) ;
}
我们可以用getenv来得到user,这样就能让一个程序只能我这个用户来执行,其他人执行不算数了
2 #include <stdlib.h>
3 #include <string.h>
4
W> 5 int main(int argc,char* argv[],char *env[])
6 {
7 const char *who = getenv("USER");
8 if(who==NULL) return 1;
9 if(strcmp(who,"lbl")==0)
10 {
11 printf("这是程序的正确执行逻辑\n");
12 }
13 else
14 {
15 printf("ONLY LBL\n");
}
}
如上面代码,这样的话就能用环境变量确定登录的用户了,如果用户不是我,就不能执行
ok下面介绍第三种方法
environ
它就是一个全局指针,我们那个环境变量不是会在bash形成一个指针数组吗?
我们char** environ指向的就是指针数组的第一个指针
enviorn[i]就表示指针数组第i个元素,可以用百分号s打印
我们用代码实现一下
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern char** environ;
int main(int argc,char* argv[])
{
for(int i = 0;environ[i];i++)
{
printf("env[%d]->%s\n",i,environ[i]);
}
}
5.理解环境变量的特性
上面三个获取环境变量的方式,都充分体现了环境变量的全局性
我们尤其再说一下第三个方法
extern char** environ;
就算是我们再开一个子进程,照样能用这个environ
不信我们就试试,充分说明了environ的全局性
1 #include <stdio.h>
2 #include <stdio.h>
3 #include <unistd.h>
W> 4 int main(int argc,char *argv[],char* env[])
5 {
6 if(fork()==0)
7 {
8 for(int i = 0;env[i];i++)
9 {
10 printf("env[%d]-> %s\n",i,env[i]);
11 }
12 }
13 }
子进程照样可以用
补充概念
除了环境变量,我们还有本地变量
我们env是只能看环境变量而不能看本地变量的
想看到本地变量,我们必须用set
set既能查看到环境变量,也能查看到本地变量
bash会记录两套变量 1.环境变量 2.本地变量
本地变量只在bash内部用,不会继承给子进程
我们本地变量可以用作shell脚本
PS1就是我们显示在开头的
PS2就是shell显示我们续行
删除本地变量命令
unset i
同样我们也可以把本地变量直接导入到环境变量,export i即可
我们环境变量一般都在bash里,那么问题来了,如果我们export是子进程,那子进程凭什么能修改我们父进程呢?
不是说进程之间是独立的吗,其实呀,export是一个内建命令,是我们bash自己调用系统调用,而不需要创建子进程的