四、再谈环境变量
1、深入理解环境变量表
在上面我们有谈到过,对于每个用户而言都有属于自己的环境的环境变量表
在环境变量表中的每一个,都有各自的用途
- 有进行路径查找的
- 有进行身份验证的
- 有进行动态库查找的
- 有用来确认当前路径的
环境变量在启动的时候就已经在内存中,所以它是一张内存级的表,里面的结构都是kv式的
那么环境变量所对应的数据是从哪里来的呢?
- 答:是从系统的相关配置文件中读取进来的,首先我们可以来看看当前家目录下的
.bashrc
这个文件,其主要是用来设置Bash shell
的一些环境变量和别名
- 还有一个的话是
.bash_profile
,其和.bashrc
类似,也是存放一些系统自带的环境变量
- 还有的话则是在根目录下的
etc
配置文件下的bashrc
这个文件,这里面所存放的便是当我们在启动系统的时候会自动加载的一些配置项
- 例如说像这里就是我们在前面所说到的【环境变量表】中的路径
- 像这个的话就是我们在 Linux权限 一文中所提到的
umash
掩码,对于普通用户而言默认为002
,这些都是当系统在一加载的时候就会自动进行配置的
- 包括的话像我们在切换使用【超级用户】和【普通用户】的时候看到它们的命令提示符并不相同,仔细观察可以发现【超级用户】为
#
,而【普通用户】的话则为$
。对于这些内容的话也是通过系统启动的时候会自动去进行配置的
问:这个内存级的环境变量究竟在哪里?
- 当我们在键入命令要运行的时候,
bash
就会去执行我们的指令。那bash
除了支持执行命令外,它也支持命令行式的自定义变量,例如下面我让命令行执行了一个自定义变量并且赋了初始值的情况,然后再去环境变量里面查找就可以发现确实是存放进去了
- 所以从下图我们可以看到当我们通过
shell
去执行一个命令行myval=100
时,假如我们又在前加了export
这个关键字,那么其就被放入环境变量中了,通俗易懂一些的话就是我们使用[malloc]
在堆区中开辟出一块空间,然后将这个字符串给存放进去。 - 我们又知道,对于 shell 来说是一个进程,用来 读取命令和命令行,因此所有的命令都是shell的子进程,所以当它执行命令的时候 ,就相当于是父进程在执行子进程,会做
- fork创建进程
- 让父进程给子进程传参
所以我们可以这么来进一步深入理解环境变量👇
💬 它即为shell内部维护的一张表,我们也称之为内存级的一张表,换句话说【环境变量表】在shell当中。然后当我们再创建执行我们对应新的子进程的时候,它就会自然而然将其所中的环境变量交给子进程
那要怎么去证明呢?
- 我们现在来执行一下下面的这句命令,往环境变量中通过
export
导入一个参数
export hello="youcanseeme"
- 然后我们通过
env
再去查看一下可以发现它确实存入【环境变量表】中了,在这里就是因为shell将其内容添加到了表中,所以shell启动的时候是从系统的配置文件中读取的环境变量表
那现在又有第二个问题了:如何去证明它会被子进程所继承呢?
- 还记得我们刚才讲到的一个东西叫做
getenv()
,可以获取到环境变量表中指定的内容,那么当我们使用./
去执行的时候,我们当前的这个进程就变成了bash
的子进程,然后当我们按下Enter
的时候,如果真的如刚才所说父进程会把自己的环境变量表经过一定的参数方式交给子进程,那么就相当于让子进程也能获得我们刚刚在环境变量表中所存放进去的内容。
💬 所以就印证了那句话,环境变量可以被所有的子进程给继承的
- 那接下去我们再来看一种现象,当我们在向shell写命令的时候,不加这个
export
的时候,即没有将内容导入环境变量中。不过呢我们可以看到echo $hello1
可以将内容打印出来,所以我们可以知道bash
其实是记录了这个变量了的,只是呢没有被添加到环境变量表里罢了 - 那么对于这种变量我们就可以称之为【本地变量】
那此时我要继续质问了:echo也是一条指令,在指令在执行的时候应该创建子进程,如果其创建了子进程,本地变量不可以被子进程继承,所以echo就不应该打出来其所对应的内容,但为什么它打印出来了呢?
- 这个知识呢涉及到Linux里的一个知识叫做【内建命令】,后续讲。。。
2、命令行参数的意义
好,最后的话我们再来拓展一个东西叫做【命令行参数】
- 这个的话我们上面在讲 环境变量的组织方式 时有所提及,即这个
argc
和argv
,首先我来说一下它们分别是什么意思
- 【argc】: 指定参数个数
- 【argv】: 代表参数选项
int main(int argc, char* argv[])
- 在知道它们分别是什么意思后呢,我们就可以通过代码的形式展现出它们的作用
int main(int argc, char* argv[]) { for(int i = 0;i < argc; ++i) { printf("argv[%d]->%s\n", i, argv[i]); } }
- 那此时我们便可以通过这两个参数,去获取到所执行命令行中的相关内容
- 那我们还可以看得更详细一些,在代码行中加上这一句,我们可以做到追踪打印当前所有的命令行参数个数以及所有的内容
printf("argc: %d\n", argc);
想必在通过以上的展示后你就可以初步地感受到命令行参数的魅力所在了
- 但是呢光就这么去看的话还不是很人性化,我们可以考虑再做一个 菜单选项,让用户去进行对应的选择,或者呢让用户一定要输入至少2个命令选项,否则的话就直接终止程序
以下是本次测试的示例代码:
void Usage(const char* name) { printf("\nUsage: %s -[a|b|c]\n", name); exit(0); } int main(int argc, char* argv[]) { if(argc != 2) Usage(argv[0]); if(strcmp(argv[1], "-a") == 0) printf("打印当前目录下的文件名\n"); else if(strcmp(argv[1], "-b") == 0) printf("打印当前目录下文件的详细信息\n"); else if(strcmp(argv[1], "-c") == 0) printf("打印当前目录下的文件名(包含隐藏文件)\n"); else printf("其他功能, 待开发\n"); }
- 运行之后可以看到,当我们直接去运行这个程序的时候,因为没有添加参数,所以程序直接终止了,而且打印出了可供选择的菜单项,接下去在我添加上这个命令选项参数的时候,在选择了对应的参数之后就打印出了对应的内容
那有同学问,说了这么久,还不是只能在Linux下运行吗,Windows下可不行哦!
- 这一块的话读者可以去Windows下的【命令提示符】,简称:
cmd
看看,键入下面这句话看看
shutdown /?
- 于是呢我们就可以看到,也是会出现许多的命令行选项供我们选择,每一个选项都对应着不同的含义(具体地读者可以自己去试试看)
💬 以上呢就是我们所要讲的【命令行参数】
五、总结与提炼
最后来总结一下本文所学习的内容:book:
- 首先在开篇,我们提到了环境变量的基本概念,知道了如果我们要去运行一个程序的话就需要将其路径添加到环境变量中,当系统的环境变量中有了这个路径之后当我们在执行此程序时它就可以去识别到路径下有这个内容
- 那如何添加呢?通过
export PATH=$PATH:/路径
的方式即可,不过这样的方式会使得在重新启动服务器之后依旧产生丢失的现象,所以我又想了一种直接通过cp
拷贝的形式去进行,在服务器重启之后此环境变量依旧可以存留 - 再者我们就要考虑到如何去通过代码如何 获取到这些环境变量,首先对于环境变量相关的命令大家要知晓,其中
echo
、export
、env
这些是会频繁使用。并且呢很重要的一点是对于不同用户来说环境变量是会不同的,会随着用户的登入而自动为其配置环境变量 - 在了解了基本的概念之后我们就可以通过代码去获取到相关的环境变量,分别是有:
命令行第三个参数
、第三方变量environ
、通过函数获取
,其中大家重点要掌握的是第三个方式,学会使用getenv()
这个方法去获取到相关的环境变量 - 但是呢光就这么去获取的话我们还无法深入地理解到【环境变量】的含义,于是我们开始去了解一些配置文件,看到了很多在我们启动系统时就会进行配置的内容,于是更加进一步地了解到了确实有【环境变量】这个东西的存在。不仅如此,我们还通过
shell所执行的命令
去观察,因为这些命令都是shell的子进程,所以当它执行命令的时候 ,就相当于是父进程在执行子进程。那通过执行的结果我们可以看出shell
将自己的环境变量表交给了执行的子进程,继而它可以去获取到表中对应的内容