环境变量是指操作系统中用来指定操作系统运行环境的一些参数;
实际上,我们在shell中使用的各种命令实际上都是一个个二进制文件,它们都在 shell 的默认搜索路径 /usr/bin 下;
比如 'ls' ,'pwd','whoami'等等;
比如我们在命令行里输入: ls /usr/bin 时,我们就能够查看到我们平时会使用的一些命令;
但是我们平时编译出来的二进制文件却不能像这些二进制文件一样,能够直接使用:
只能够通过 "./myprocess" 的形式来使用我们编译的二进制文件;
实际上这是因为环境变量的存在才导致我们能够直接使用命令;
而环境变量实际上就是我们登录到操作系统时,操作系统自己根据配置来
常用的环境变量
1.PATH:指定命令的搜索路径
2.HOME:指定用户的主工作目录
3.SHELL:当前的shell,值通常是 /bin/bash
4.USER:当前用户
之所以我们能够直接使用命令就是因为 PATH 命令的存在;
当我们 不带 ' ./ ' 而是直接输入一个二进制文件的名称的时候,操作系统就会根据PATH提供的路径去寻找是否有该二进制文件;
而带上 './' 就表示二进制文件在本文件夹,不用在PATH中去寻找该命令;
那么我们怎样做才能使我们所编译的二进制文件可以直接使用呢?
1.将二进制文件直接放入 PATH 中的默认路径下(不推荐)
2.更改 PATH 的默认路径为当前我们所编译的文件的路径(不推荐)
3.添加当前编译的文件的路径到 PATH 中去
第一种方式会污染我们操作系统的指令池,并不推荐;
第二种方式会导致其他所有命令都无法使用,所以不推荐;
而第三种方式只是添加了一个路径,所以可以使用;
但是,我们虽然能够修改或添加PATH的默认路径;
但是我们每次重新登录 shell 的时候,这个PATH都会变回原来的模样;
这是 shell 里面配置好的文件决定的;
那么我们第三个方法应该怎么做呢?我们先要了解环境变量的相关命令
有关环境变量的命令
1.echo:显示某个环境变量值(想要查看某个环境变量必须在环境变量名前加 '$')
2.export:设置一个新的环境变量值
3.env:显示所有环境变量值
4.unset:清除环境变量
5.set:显示本地定义的shell变量和环境变量
我们先来看看PATH的默认搜索路径:
我们发现,默认搜索路径都是有 ':' 隔开,实际上我们在bash上输入命令的时候
shell就是根据 ':' 隔开的路径来查找指令;
而我们的第三种方法就是通过export来更改当前的 PATH ;
可以看到,更改了PATH之后,PATH后面多了新的搜索路径
而我们自己编译并运行的文件也能够直接运行了;
当然,我们这里只是暂时的更改,当我们重新登录shell,就会被更改回原来的样子;
环境变量的特性——全局性
在代码中,全局变量能够被各个代码块所使用,而环境变量和这类似;
它能够被子进程所继承;
我们都知道我们编译并运行的进程都是bash进程的子进程,那么我们应该能够通过子进程查看父进程的环境变量,这里我们能够通过系统调用来获取环境变量——getenv;
getenv
传递所查的环境变量的名字,找到就返回该环境变量的内容,没找到就返回null;
接下来我们实验一下
#include<stdio.h>
#include<stdlib.h>
#define PATH "PATH"
int main()
{
char * myenv = getenv(PATH);
printf("PATH = %s\n",myenv);
return 0;
}
在这里,我们使用 getenv 函数获得对应环境变量所对应的内容并输出;
这样我们就能够清楚的了解到,环境变量是能够继承的;
我们都知道bash也是一个进程,我们能够直接用它创建一个本地变量;
但这个本地变量无法继承给子进程
这里我们创建了一个 myval 的本地变量,它的内容为123;
#include<stdio.h>
#include<stdlib.h>
#define PATH "PATH"
#define myval "myval"
int main()
{
char * myenv = getenv(myval);
printf("myval = %s\n",myenv);
return 0;
}
但是我们可以看到,它并非是环境变量;
但是我们可以通过export将它变为环境变量;
这里的export可以将局部变量直接转化为环境变量而不修改它的内容;
但是若是没有内容则需要添加内容;
我们现在知道环境变量已经有了全局性,子进程能够继承父进程的环境变量;
那么是如何做到的呢?
首先需要了解环境变量的组织方式
环境变量的组织方式
每一个程序,都会有一张环境表,它是一个字符指针数组,它的每一个指针都指向一个字符串;
就像这样,而我们之前学习的时候,知道,main 函数实际上有三个参数
1.int argc;
2.char* argv[];
3.char* env[];
其中的env就是一个环境表,其内部包含各种环境变量;
而我们的bash实际上也是一个程序,它也有它的main函数,这就是环境变量继承的原因;
那如何证明呢?
我们写下这样的代码:
#include<stdio.h>
#include<string.h>
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;
}
通过env查看环境变量;
通过创建的程序查看环境变量
我们发现,除了最底下的程序所在的路径不同之外,其他的环境变量都是一模一样的;
这就是环境变量继承的原因;
扩展
上面提到 main 函数还有两个参数 argv 和 argc;
那么这两个参数是干什么用的呢?
这两个参数实际上是命令行参数;
argv是命令行参数的指针数组,而argc则是argv的大小;
#include<stdio.h>
#include<string.h>
int main(int argc,char* argv[],char* env[])
{
for(int i = 0;i<argc;i++)
{
printf("argv[%d] = %s\n",i,argv[i]);
}
return 0;
}
myprocess:test.c
gcc -o myprocess test.c -std=c99
.PHONY:clean
clean:
rm -f myprocess
我们发现,argv[0]表示命令,后续的指针则是表示一个个指令;
这就是我们 bash 的真面目;
通过 argv 数组获取我们的命令,然后将指令一个一个分割;
最后通过指令找到对应的操作;
并且这些指令也能够继承!
总结
1.我们的环境变量是操作系统用来控制操作系统运行环境的一些指令;
比如默认搜索路径,当前使用用户等;
2.环境变量具有继承性,通过main函数的env指针数组继承;
3.我们的bash是通过env指针数组继承父进程的环境变量;
4.bash的命令则通过分割main函数的argv指针数组的命令和指令进行不同的操作;
5.main函数的所有参数都是可继承的;