文章目录
- 环境变量引入
- 初见环境变量
- 和环境变量有关的指令
- 如何通过代码获取环境变量
- getenv()
- main函数的命令行参数
- 第三方变量environ
- 程序变量可以继承给子进程
环境变量引入
Linux中有各种指令,
每个指令其实都是一个可执行程序:
和我们自己写的C语言代码编译生成的可执行程序一样。
当我们在运行自己的可执行程序时需要加上路径./exe
,
其实就是为了让命令行解释器知道我们要去执行哪个程序,
但是当我们执行某条指令时却不需要加路径限制,
命令行解释器好像自己就能找到这条指令对应的可执行程序执行它。
而做到这一点其实就是因为环境变量的存在。
初见环境变量
对于上面提到的那种情况,其实是因为环境变量PATH的存在。
PATH是指定命令的搜索路径,
我们可以通过echo $PATH
的形式来查看:
可以看到PATH有好多路径,
不同路径间通过 冒号 “ : ” 分割,
可以看到红框框出来的就是此前man指令所在的目录。
当然环境变量还有很多,
比如我们使用pwd命令输出当前目录时,
pwd是一个可执行程序,与我们所在的那个目录没有任何关系,
它是怎么能实时知道我们在哪个目录的呢?
其实还存在一个名为PWD的环境变量:
又或者使用指令cd ~
会跳到用户所在目录,
可执行程序cd又是怎么知道我们的用户目录是哪呢?
其实还存在一个名为HOME
的环境变量:
和环境变量有关的指令
echo
: 显示某个环境变量值
上面提到了,我们可以用
echo $name
的方式来输出一个环境变量的内容。
env
: 显示所有环境变量
想看看所有的环境变量?
env(environment variable)
满足你:
export
: 设置一个新的环境变量
首先我们可以在命令行定义变量:
但此时a只是一个本地变量,我们无法通过env命令查看到它
一个办法就是用export命令把a设成全局变量:
当然,我们还可以用export来修改环境变量。
当前这个目录下面有一个输出hello world的可执行程序my_test,
我们将my_test所在目录导入环境变量PATH中:
export PATH=$PATH:<路径>
这样运行my_test的时候就不用加路径了:
但是这样做只是临时的,如果再开一个shell就不支持了,
因为这样定义的只是在当前命令行进程中定义的,
对其他命令行解释器没有影响。
当然,如果想创建一个自己的指令,
也可以将可执行程序拷贝到系统PATH指向的目录中。
unset
: 清除环境变量
我们可以用unset + 变量名清除一个环境变量或本地变量:
当然也可以把环境变量清除掉:
如果不小心将PATH等系统提供的环境变量清掉了不要担心,重开就好了:
set
: 显示本地定义的shell变量和环境变量
我们可以用env打印所有环境变量,
同时也可以使用set打印所有的本地变量和环境变量:
如何通过代码获取环境变量
getenv()
上面我们可以通过echo、env等方式从命令行获取环境变量,
我们可不可以通过自己写的程序获取环境变量呢?
和用系统调用接口getpid()
获取进程的PID一样,
Linux同样提供了接口getenv()
获取特定的环境变量:
RETURN VALUE
The getenv() function returns a pointer to the value in the environment, or NULL if there is no match.
description那儿提到了一个叫environment list的东西,
这其实是一个环境表,本质上就是一个字符指针数组,每个指针指向一个环境变量:
这其实就是环境变量的组织方式,每个程序都会这样收到一张环境表。
下面就用一下试试:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char* path = getenv("PATH");
printf("path=%s\n", path);
return 0;
}
main函数的命令行参数
main函数其实也能有参数:
int main(int argc, char *argv[], char *env[])
这三个参数叫做命令行参数。
下面对这三个命令行参数进行一一剖析。
首先argc和argv是一块的。
可以看出argv是一个字符指针数组,它有几个元素呢,就是argc个。
当我们什么也不做时看看它都有什么内容?
什么都不做时它只有一个信息,就是运行程序时使用的路径位置。
我们再试着运行test时加个命令行参数:
所以为什么我们使用ls、rm等命令时使用不同的命令行参数会有不同的运行效果,
这个小实验应该能给出我们答案。
下面再看第三个参数env。
上面我们提到了,每个程序都会收到一张环境表,
也就是一个指针数组,env其实就是指向这张环境表的。
所以我们可以通过遍历env来获取所有的环境变量:
第三方变量environ
当然,main函数可以用env来获取环境变量,
但还可以通过外部变量envrion来获取,它和env并无本质差别,
都是指向environment list的一个指针。
libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时要用extern声明:
程序变量可以继承给子进程
我们可以试着一个本地变量,并用export将它导成环境变量:
然后我们试着在代码中用getenv()
接口获取我们定义的环境变量:
发现获取到了,这里可以说明一点,环境变量是可以继承给子进程的。
为了加强验证这点,再看下面的现象:
右边那个是新开的终端,结果就截然不同。
我们称左边的命令行为bash1,右边的命令行为bash2,
可以看出来,my_val是定义在bash1中的一个环境变量,
在bash2中并不存在。
在bash1中运行的进程test是bash1的子进程,
在bash2中运行的进程test是bash2的子进程,
而只有bash1的子进程拿到了my_val。
另外,我们重新定义my_val为本地变量,
试试命令行运行的子进程是否还能拿到:
说明只有环境变量才可以被子进程继承,本地变量不可!
这样就有一个问题,按理来说echo也是bash的一个子进程,
那为什么echo就可以获取到本地变量呢?
Linux下大部分命令都是通过子进程的方式执行的。
但是,还有一部分命令,不通过子进程的方式执行,而是由bash自己执行,这种命令叫做内建命令,而echo就属于这种内建命令。