Linux命令行参数和环境变量
1. 命令行参数
1.1 引出问题
我们学习C语言的时候,写main函数的时候一般都是没有带参数的,但是main函数既然是一个函数,也是被调用的,按理来说也能和其他函数一样,也能带参。
事实也是如此,main函数是可以带参数的,但是并不是随意带的,而是以这种格式来带的int main(int argc, char *argv[])。
但是这些参数又是什么意思??
char *argv[]是一个char指针数组,这个数组中的每一个char*指针都分别指向一个字符串。int argc是一个整数,代表argv中元素的个数。
1.2 测试
我们使用一个简单的代码来测试一下这些指针分别指向什么字符串:
Linux命令行参数和环境变量
1. 命令行参数
1.1 引出问题
我们学习C语言的时候,写main函数的时候一般都是没有带参数的,但是main函数既然是一个函数,也是被调用的,按理来说也能和其他函数一样,也能带参。
事实也是如此,main函数是可以带参数的,但是并不是随意带的,而是以这种格式来带的int main(int argc, char *argv[])。
但是这些参数又是什么意思??
char *argv[]是一个char指针数组,这个数组中的每一个char*指针都分别指向一个字符串。int argc是一个整数,代表argv中元素的个数。
1.2 测试
我们使用一个简单的代码来测试一下这些指针分别指向什么字符串:

运行:

通过上面的测试,我们可以很容易发现,argv[]里面的指针指向的字符串,就是我们在命令行中运行这个程序时输入的参数,其中第一个字符串代表程序的路径+名称(./xxxxx),后面的参数代表我们运行程序时输入的选项,以空格作为分隔符。
在argv数组中,实际上是有argc个元素的,最后一个元素指向NULL,我们可以修改一下代码进行测试:


1.3 作用
这个功能有什么意义呢,这时我们想想,我们之前使用的Linux命令,比如:ls、cd、cp、mv......我们在使用这些指令的时候,都是可以带参数的,有些指令带不同的参数就会有不同的功能,我们之前说过,我们使用的这些指令本质上就是一个个可执行文件,那么我们也就可以像这些指令一样,编写我们自己的程序,通过命令行参数来实现,输入不同的选项,就可以实现不同的功能。
1.4 原理
我们之前说过,我们在命令行中启动的进程的父进程都是bash,而且之前说过,子进程在被创建的时候,继承的是父进程的代码和数据,而我们在命令行中输入的东西,都是给bash的,而bash是命令行解释器,它会解释我们输入的字符串,处理好,然后执行。所以我们输入的这些参数,实际上是由bash构建成了一张表,然后继承给子进程,子进程自然也就能读取到父进程中的表的内容。
2. 环境变量
2.1 引出问题
我们使用的自己写的程序都需要带路径来执行,但是使用Linux指令时为什么可以不带路径直接使用?Linux指令本质上不也是程序吗??
原因是,Linux系统中存在一些全局的设置,用来告诉命令行解释器,应该去哪个路径下去查找这个命令。
而这个全局的设置就是环境变量。
2.2 查看环境变量
PATH就是环境变量的其中一个,我们可以通过echo $PATH来查看PATH的内容(类似于PATH这种大写的就是环境变量,查看环境变量的内容需要在环境变量的名字前面加上$符号):

这个配置,在我们登录Linux系统时,就从配置文件中加载到bash进程中的(内存中)。
其中这里面的内容是一个个路径,以:分隔,bash在执行命令时,会先在这些路径下搜索命令,从第一个开始找,找到了就执行,如果没找到,就会提示没有找到该命令,我们使用的Linux指令(如ls、cd......)基本都是在/usr/bin路径中的,所以我们在执行这些命令时,bash就会到PATH中的路径去找命令,然后再/usr/bin路径下找到命令,然后执行。
2.3 修改环境变量
测试代码:

那么我们是否可以让我们执行自己的程序像执行系统指令一样,无需带上路径?
当然是可以的:
-
方法一:将我们的程序拷贝到当前已有的环境变量的路径下(不推荐)。

-
方法二:将当前路径添加到PATH中(
PATH=$PATH:我们的路径)。
但是,上面说过PATH是在我们登录时从配置文件中加载到内存里的,所以我们当前修改只是内存级的,如果我们退出再登录,这个配置就不在了。
-
方法三:修改配置文件(~/.bash_profile)。
先看看这个文件:

我们可以看到,这里面不就是我们的PATH吗?此时,我们添加路径只需要添加到PATH那一行的后面:

然后重新登录,我们就会发现,PATH中就自动加载了我们对应的路径:

2.4 其他环境变量
环境变量并不是只有PATH,PATH只是众多环境变量的一种,我们可以使用env命令查看所有环境变量:

我们介绍其中几个环境变量:
-
HOME:这个环境变量就是我们当前用户的家目录
-
PWD:就是我们当前所处的路径(动态变化)
-
SHELL:bash所处的路径
-
HISTSIZE:系统默认记录用户最新使用的命令的数量(我们可以history查看我们的历史命令)
-
HOSTNAME:我们的主机名
-
LANG:当前编码方式
2.5 自定义环境变量
2.5.1 添加环境变量
export 环境变量名=环境变量值

内存级添加,后续登录就没了。
【注意】如果不添加export,直接使用环境变量名=环境变量值,添加的是本地变量。
2.5.2 删除环境变量
unset 环境变量名

内存级删除,不影响后面的登录。
2.6 整体理解环境变量、系统
2.6.1 C语言获取环境变量
在系统中存在一个全局变量environ,这个环境变量是一个二级指针。

这个二级指针指向的东西就是一个个一级指针,而每个一级指针又指向一个字符串,每个字符串就是一个环境变量,environ指向的最后一个一级指针是NULL代表结束(像极了前面的argv)。
(其中的的一些其他)
【注意】使用environ之前,需要先使用extern声明。
测试代码:


这个和我们刚刚使用env查到的环境变量是一模一样的。
这个和命令行参数类似,在bash启动的时候,从配置文件中加载到自己的数据中,然后,我们使用命令行运行程序的时候,程序也就继承了bash的数据,也就能看到bash加载的环境变量。
实际上,我们的main函数也是可以有第三个参数的。

我们也可以通过这种方式来获取环境变量。

有了上面的环境变量表,我们就可以理解,前面使用export导入环境变量实际上就是在env表上加上一条数据。
我们不仅可以通过上面的方式来获取全部的环境变量,也能通过getenv来获取指定的环境变量。

【总结】代码获取环境变量的三种方式:
- 通过全局变量environ获取
- 通过main函数参数env获取
- 通过getenv函数获取
2.6.2 理解
我们所有运行的程序,都是在bash下运行的,也就是所有的进程都是bash的子进程,自然这些子进程也就能继承bash的数据。
因此,环境变量具有系统级的全局属性,因为环境变量本身会被子进程继承下去。
这里还有一个问题:我们使用export添加环境变量也是命令,按理来说也是创建子进程来完成的,既然如此,子进程中修改的数据怎么可以被父进程看到???
其实实际上有一小部分命令不是由bash创建子进程来完成的,而是bash自己完成的,比如export、echo......像这样由bash亲自执行的命令就叫做内建命令。
如何判断哪些命令是内建命令?将PATH置为空,还能运行的命令就是内建命令,也就是不需要找到程序去运行的命令,就是内建命令。
上面说的本地变量,使用env是查不到的,同样,在代码中使用getenv函数也是查不到的,但是我们通过echo可以将这个变量的内容打印出来,说明这个变量是存在的,只是没有被放到env表中,我们也可以通过export 本地变量命令将本地变量导入到env表中(本地变量只在bash内部有效,无法被子进程继承下去,所以子进程无法查到,echo能看到,所以echo是内建命令)。