【Linux 系统】命令行参数和环境变量
文章目录
- 前言
- 1. 命令行参数
- 2. 环境变量
- 2.1 概念
- 2.2 进程中获取环境变量
- 2.3 环境变量的操作
- 2.4 内建命令
- 总结
前言
我在bash命令行上执行一些指令的时候,例如:ls
指令。我们可以让我们的指令带上选项!从而实现出来不同的功能,可是我们有没有想过为什么指令带上不同的选项之后,就可以实现不同的功能了呢?
在这篇文章中,小编和大家了解认识两个东西:
-
命令行参数
-
环境变量
1. 命令行参数
- 首先我们来看,
main
函数的函数原型:
int main(int argc, char* argv[])
{}
main
函数也是有参数的:
argc
:是一个整型参数,代表数组的元素个数。argv
:是一个指针,指向的是是一个char*
一维数组的起始地址。
我们对于这两个参数还是保持着一种好奇心理,于是我们写出了下面这样的测试代码:
// 已经正确包含头文件
int main(int argc, char* argv[])
{for(int i = 0; i < argc; ++i){printf("[%d]: %s\n", i, argv[i]);}return 0;
}
我们编译运行一下,得到如下的结果:
-
现象:
我们在命令行上的参数都被传给了我们启动的进程的
main
函数的参数(argv)
不妨现在,我们将我们的代码进行更改:
int main(int argc, char* argv[])
{if(strcmp(argv[1], "-a") == 0){printf("功能1...\n");//实现功能}else if(strcmp(argv[1], "-b") == 0){printf("功能2...\n");}else{printf("未实现该功能...\n");//...}return 0;
}
我们来看我们的测试结果:
-
结论:
命令行上的选项我们可以通过给main函数参数的形式交给程序。程序内部可以通过解析判断指令的选项类型是否正确,从而给我们实现不同的功能。
-
问题:那么我们在命令行上启动的程序是如何传参交给
main
函数的呢?这个问题,我们在命令行启动的进程大多都是bash进程的子进程。我们bash进程在接受到用户的命令的时候可以进行判断,从而创建子进程,采用系统调用(
exec*
系列进程替换接口,详情在进程控制),从而将我们的输入的命令行选项传参给子进程。 -
为什么需要
main
函数有这样的参数呢?就是为我们的指令、工具、软件等提供命令行选项的服务!
2. 环境变量
学习Linux系统,必然需要了解的一个知识点就是环境变量。在进程等中都比较重要。
2.1 概念
-
是什么?
环境变量是系统提供的一组 name=value形式的变量,不同的环境变量有不同的用处,一般而言,环境变量具有全局性。
-
认识环境变量:
-
PATH
:路径查找的环境变量来看这样一个示例,当我们在命令行上不指明路径执行我们自己的代码的时候:
对于我们的命令,报错信息是:not found
也就是意味着,我们的bash一定是曾经去一个地方找过这条命令是否存在!这个地方就是环境变量的PATH
。查看一下:
echo $PATH
所以想要我们的程序不通过指明路径指向特别简单:-
将我们的命令软连接/安装到环境变量的文件路径下。
-
将我们当前文件安装的
PATH
中。
我们采用第二种方式演示一下:
注意:这样更改环境变量PATH
只是临时的,重新启动会话就会失效了。 -
-
USER
:表示用户身份的环境变量我们的bash命令行是如何识别一个用户的身份信息的呢?可以通过环境变量
USER
来识别,在代码级别做判断。-
首先我们来看一个接口:
getenv
:
作用:根据传入的name获取value。 -
那么一个用户的身份我们可以做如下的判断:
我们通过上面代码,可以认识到,bash在创建子进程之前(我们程序执行指令之前)可以对我们的身份信息做出判断的!!
-
-
-
指令显示当前系统的环境变量:
env
2.2 进程中获取环境变量
-
第一种方法:我们刚刚已经谈到了,我们可以通过函数调用:
getenv
获取单条环境变量的value值。 -
第二种方法:main函数参数的第三个参数
实际上,
main
函数还有第三个参数。原型如下:int main(int argc, char* argv[], char* env[])
。最后一个参数就是指向的就是系统为每一个进程维护的一张环境变量表!
我们可以尝试打印出来试试(一般最后一个参数为nullptr
)
// 正确包含头文件
int main(int argc, char* argv[], char* env[])
{for(int i = 0; env[i] != NULL; ++i){printf("[%d]:%s\n", i, env[i]);}return 0;
}
我们依然可以获得环境变量表:
-
上图有一个细节:此时我们设置的环境变量
PATH
还在!!那么我们一定会有如下问题:
-
我们在bash命令行上启动我们的进程,我们的进程环境变量表来自哪里?
这个问题,上面的哪个示例也已经能窥探一二了:我们在bash上启动的进程都是子进程,所以当我们子进程在被启动的时候,环境变量表来源于我们的父进程bash。
同样地,当我们的进程创建了一个子进程之后,我们创建的子进程就会得到来自我们父进程的环境变量表。为什么?因为环境变量表也是数据,子进程会共享父进程的代码和数据。 -
环境变量表是谁传递参数的?编译器对这些参数是如何感知的?
我们也能明白,在我们bash命令行启动进程的时候父进程就需要进行准备,当我们父进程创建子进程之后,采用函数调用
exec*
系列接口的时候,我们的Linux操作系统就会为我们传递参数!编译器(如 gcc)编译代码时,它不只是编译
main
函数。它会将一段特殊的启动代码 链接到可执行文件的开头。这段代码是用汇编和C语言写的,负责设置C程序运行所需的基本环境(在准备的时候就可以获取环境变量表的地址了,还有argc
,argv
等参数)。当我们通过exec*
接口替换你的程序后,CPU指令指针首先指向的就是这段启动代码,而不是main
函数。简单来说,main函数也是函数,也是在启动的时候被调用的! -
一般一个进程的环境变量表存储在进程地址空间的哪个位置?
根据操作系统的视角来看,一般存储在一个进程地址空间的顶部。所以,环境变量表在进程的角度也是一个进程的数据。
-
-
第三种方法:采用全局变量
extern char **environ
。该全局变量被声明在:<unistd.h>
- 注意:在同一个进程中,第二三种方法获取到的表一定是同一个。在程序替换中会涉及到对环境变量表的替换。
2.3 环境变量的操作
我们也可以来了解一下如何设置环境变量和查看环境变量
echo $name
:显示环境变量名为name的value值。env
:显示当前系统的所有环境变量。export
:设置一个新环境变量。unset name
:清除名为name的环境变量。set
:显示本地自定义的shell变量和环境变量。
-
export
:
说明我们的环境变量已经设置成功了!
2.4 内建命令
-
来看下面一个测试:
原因也很简单,这涉及了一个命令的分类。
- 常规命令:创建子进程完成的命令。
- 内建命令: bash不创建子进程完成,而是自己完成。类似于自己调用了自己内部的函数!
很显然,上面的echo
指令是一个内建命令!
总结
我们需要了解:
-
一个程序是有两张表的:
-
命令行参数表。
-
环境变量表。
我们可以简单理解为:由操作系统维护的两张表。在进程启动的时候需要传给进程。
-