【Linux】14:环境变量
目录
环境变量是一种非常重要的概念,它对于系统的行为和应用程序的运行起着关键的作用。
那么环境变量究竟在扮演着什么样的角色呢?
为什么我们在编写C/C++代码的时候,不需要显式的告诉编译器我们所需要的动静态库在那,但链接照样可以成功的生成可执行程序,其实这里就有环境变量的辅助,所以接下来,就让我们一起来学习 ----- 环境变量
一、概念介绍
1.1 环境变量的基本概念
- 环境变量(environmentvariables)一般是指在操作系统中用来指定操作系统运行环境的一些参数
- 如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
- 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性
1.2 命令行参数的介绍
首先,我们通过一个问题来引出:
main是一个函数,main函数有参数嘛?
答案是:main是有参数的!!!main可以有0个参数或者2个参数,或者3个参数!!
我们平常写main函数的时候都没有写main函数的函数。
当main函数有两个参数的时候,这两个参数分别是什么呢?
答案是int类型的argc和char*类型的argv。其中,argv是一个指针数组,里面存放了字符串的地址,叫做命令行参数表。而argc是argv中的元素个数。那argv里面存放的是什么呢?
我们写一个测试代码来看一下,如下所示:
1 #include <stdio.h>
2
3 int main(int argc,char* argv[])
4 {
5 for(int i=0;i<argc;i++)
6 {
7 printf("argv[%d]=%s\n",i,argv[i]);
8 }
9 return 0;
10 }
我们使用gcc来编译:gcc code1.c -o code1
然后使用./code1来运行,如下所示:
![]()
我们发现argv里面存放的是我们刚刚输入的命令,也就是将字符串./code的地址存到了argv数组里面。如果我们输入./code1 a b c d呢?如下所示:

我们发现argv里面存放的是的我们刚刚输入的命令,以空格来区分字符串,将我们输入的命令存储下来。如下所示:

当我输入命令的时候,xshell,也就是bash会帮我们把命令以空格区分,将这几个字符串的指针存储到agrv数组中。并且argv是以null结尾的,里面的元素个数有argc个。
这个code1是我们编写的程序,我们运行它需要带上./,通过相对路径来找到这个程序,那为什么我们使用系统命令的时候,如:ls pwd cd 这些命令不需要带上路径呢?系统提供给我们的命令本质上不就是程序吗?和我们写的code1本质上是没有任何区别的,为什么我们写的程序运行的时候需要加上路径,而系统提供给我们的命令不需要加上路径呢?如下所示:

我们在使用系统提供给我的命令是不需要带上路径的,也可以带上,使用我们自己编写的程序需要带上./来找到程序,这是为什么呢?
那这个跟我们的环境变量有什么关系呢?
那main函数的命令行参数有什么用呢?
首先我们来介绍一个知识点,我们在使用ls的时候是可以带上选项的,例如ls -a -l等等。
系统提供的命令都是c语言写的,我们如何实现这个带选项的程序呢?
代码如下所示:
#include <stdio.h> #include <string.h> int main(int argc,char* argv[]) { if(argc!=2) { printf("Uages:%s [-a|-b|-c]\n",argv[0]); return 1; } if(strcmp(argv[1],"-a")==0) { printf("这是功能1\n"); }else if(strcmp(argv[1],"-b")==0) { printf("这是功能2\n"); }else if(strcmp(argv[1],"-c")==0) { printf("这是功能3\n"); }else { printf("Uages:%s [-a|-b|-c]\n",argv[0]); } return 0; }运行如下所示:
代码解析:
当我们输入./code2来执行我们写的程序的时候,是没有任何逻辑的,这是因为这个程序需要带上选项,所以提示Uages:./code2 [-a|b|-c]。
当我们输入./code2 -a的时候就运行功能1的逻辑
当我们输入./code2 -b的时候就运行功能2的逻辑
当我们输入./code2 -c的时候就运行功能3的逻辑
当我们输入./code2 -d的时候,就提示Uages:./code2 [-a|b|-c]这样使用。
我们以前用到的ls -a,ls -l ,rm -r 等等,都带了选项。所以命令行参数的用途,是为了让一个程序可以通过选项来执行不同的子功能。
所以main函数的命令行参数,是实现程序不同子功能的方法!!!
这就是指令可以带选项的原理,main函数是可以有参数的!!!
所以在我们程序运行的时候,进程启动时,进程拥有一张argv表,用来支持实现选项功能。是bash帮忙创建的。
现在我们来回答这两个问题:
我们在使用系统提供给我的命令是不需要带上路径的,也可以带上,使用我们自己编写的程序需要带上./来找到程序,这是为什么呢?那这个跟我们的环境变量有什么关系呢?
我们自己写的程序和系统提供的指令,都是c语言代码,没有本质的区别!!
我们在执行一个程序的时候,必须要找到这个程序在哪里!所以我们在运行我们自己的程序的时候需要./来找到程序。为什么执行系统命令的时候不需要加上路径,只需要执行指令就可以呢?
这是因为系统中存在环境变量!!!环境变量会帮我们找到系统指令的位置!!!
我们在执行我们的程序的时候,不会在当前路径下查找,而是会在/usr/bin目录下查找,那我们将我们写的程序拷贝到/usr/bin目录下是不是也可以不要带路径了呢?
答案:是的,当我们将我们的程序拷贝到/usr/bin目录下,执行我们的程序就不用带上路径了
如下所示:

我们将code2程序拷贝到/usr/bin目录下之后,直接输入程序名字就可以执行了。
我们强烈不建议将我们写的程序拷贝到/usr/bin目录下,这是因为我们写的程序可能是有bug的,把我们写的程序放在系统的默认路径下,就可能污染系统的指令池。所以我们将/usr/bin目录下code2程序给删除。然后再次输入程序名字就找不到程序了,需要输入./才能找到程序。如下所示:

最终结论就是:我们系统是默认知道/usr/bin目录下的,那为什么系统会知道在/usr/bin目录下查找程序呢?
这是因为系统中存在环境变量,这个环境变量叫做PATH。
总结:在linux系统中,存在一个环境变量PATH,PATH会标识一段路径,当我们执行程序的时候,会默认在环境变量PATH路径下查找!!!所以PATH是linux指令和程序执行时的搜索路径!!!
1.3 查看环境变量
我们在上面将PATH环境变量引出来之后,那我们如何查看环境变量呢?
我们可以使用env命令来查看环境变量,可以将系统中所有的环境变量给我罗列出来,如下所示:

环境变量是一个变量,环境变量的构成是:名字=内容,通过名字来标识环境变量的唯一性!
这个环境变量也太多了,我们就想查看特定的PATH环境变量,如何操作呢?
我们可以使用echo $PATH来查看PATH环境变量,如下所示:

PATH输出的这个长字符串以:(分号)为划分点,分为好几条路径:如下所示:
- /usr/local/sbin
- /usr/local/bin
- /usr/sbin
- /usr/bin
- /sbin/bin
- /usr/games
- /usr/local/games
- /snap/bin
所以我们在linux系统执行命令时,就默认在这些路径下搜索,这也就解释了为什么我们写的程序需要加上./,而系统提供的命令不需要加上./,因为默认会在这些路径下查找,从1到9,依次查找!
如果所有的PATH环境变量路径下都没有找到程序,就会报command not found错误。
正是因为ls命令在/usr/bin目录下,所有我们在执行ls指令的时候不需要带路径,默认会在PATH路径下查找!!
那我们将我们自己的路径添加到PATH环境变量里面,那我们执行我们自己的程序,就不是不需要带./了嘛?
没错,只要我们将我们自己的路径添加到PATH环境变量中,那这个路径下的程序不需要带./就能执行。那我们如何将我们自己的路径添加到PATH环境变量中呢?如下:
二、如何添加环境变量
2.1 PATH环境变量
系统中已经有PATH环境变量了,我们可以直接PATH=路径,如下所示:
PATH=/home/zhang/linux113/lesson16
此时我们再次查看PATH环境变量,就为这个路径了/home/zhang/linux113/lesson16,如下所示:

我们执行我们自己的程序就不用带./了。
但是我们执行系统指令的时候,如下所示:

就会报command not found错误了。这是因为PATH环境变量修改了,我们在/home/zhang/linux113/lesson16路径下找不到系统指令了。
这也反向的证明了linux系统在查找命令时就是在PATH命令下查找的。
我们想让我们的路径添加到PATH路径中,而不是覆盖原来的路径,那怎么办呢?现在原来的路径都没有了。如何恢复呢?
我们可以直接关掉xshll,然后重新登录,此时PATH环境变量就恢复成原来的样子了,如下所示:

此时PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin,只需要关闭xshell,在重新登录,PATH路径就自动恢复了。
现在,我们只想让PATH添加/home/zhang/linux113/lesson16,而不是覆盖,如下所示:

我们先将PATH打印出来复制一份,然后在赋值的时候将拷贝的内容粘贴,在后面加上:/home/zhang/linux113/lesson16,就可以了,我们就可以在PATH环境变量中添加我们的当前的路径了,执行code2也就不需要带./了。
然后我们再将xshell关闭,再重新登录,就发现又恢复成原来的样子了,没有我们当前了路径了。
所以当我们重新登录xshell时,我们之前自己配置的环境变量都会恢复默认。
那么现在又有两个问题了,
1:从存储的角度,如何理解环境变量呢?
2:系统重新登陆,环境变量最开始从哪里来呢?
答1:实际上,环境变量是由名称+内容组成的。但是这些环境变量的值是被谁保存下来的呢?包括当我们在执行指令时,是谁在系统中查找这些命令呢?答案是bash,当我们登录的时候,系统就会给我们创建一个bash进程,而bash就必须在系统中读取我们的环境变量信息,然后在bash进程内部形成一张表,这张表叫环境变量表,也就是指针数组的样子,其实这些环境变量,一个一个的字符串,环境变量表存放字符串的地址,如下所示:
所以在bash进程启动的时候,需要构建出来一个环境变量表。然后我们输入指令时,例如ls -a。ls -a这个字符串就会被bash进程拿到,然后bash会帮忙拆分这个字符串,进而构建一个命令行参数表argv,得到程序的名字,然后在PATH路径中查找ls程序是否存在!!如果存在,就创建ls子进程、不存在就报错。
所以bash进程内部有两张表,一个是环境变量表,一个是命令行参数表。
所以环境变量就是Key/Value的常字符串,当bash启动的时候,会new一段空间来创建环境变量表来存放这个常字符串!!!bash进程会维护这张表,当执行env的时候,就打印这张表的内容,因此环境变量在bash进程的上下文里面。
答2:环境变量是从系统的相关配置文件中来的,linux系统是会存在系统的相关配置文件的。
这个配置文件会直接或间接包含所有的环境变量内容,当bash进程启动的时候。他会从配置文件中读取所有的环境变量值,并在自己的内部创建这一张表。如果我们将我们自己的目录路径系统的配置文件中,那么每次重新登录的时候,bash也会将我们的路径添加到PATH路径中。
最后,如果我们linux系统有10个用户在登陆,那么应该有几个bash进程呢?
答案是,如果我们linux系统有10个用户在登陆,就会有10个bash进程,并且每一个bash都会读取系统的相关配置文件,形成环境变量表,也就是10份环境变量表。
2.2 其他环境变量
在上面我们介绍了PATH环境变量,其实linux还有其他的环境变量,如下所示:

我们挑几个终于的环境变量讲解
2.2.1 HOME环境变量
我们在执行cd ~的时候,会回到当前用户的家目录,这是为什么呢?
这是因为环境变量表中有HOME,记录了当前用户的家目录,如下所示:

当一个用户登录的时候,会创建一个bash进程,然后将HOME环境变量设置为/home/zhang。当bash进程收到 cd ~时,会切换成为cd /home/zhang。所有HOME环境变量叫做当前用户的家目录
2.2.2 SHEll环境变量
这个环境变量指的是,用户在登录时,bash使用的时哪一个版本的shell,所以把bash的路径保持下来了。如下所示:
![]()
2.2.3 USER和LOGNAME环境变量
USER环境变量指的是当前用户是谁。LOGNAME环境变量指的时当前登录用户是谁。
我们在使用su和su -切换root身份的时候,这两个会有所不同。
当我们是zhang这个用户,然后使用su切换为root的身份,此时USER和LOGNAME环境变量都还是zhang,如下所示:

我们使用su切换为root身份的时候,USER和LOGNAME环境变量都还是zhang,并且当前路径不会发生任何变化。
当我们是zhang这个用户,然后使用su -切换为root的身份,此时USER和LOGNAME环境变量都会变成root,这是因为我们使用root的省份重新登录了,如下所示:

我们使用su -切换为root身份的时候,会让root重新登录。USER和LOGNAME环境变量都会变成root,并且当前路径变成了/root。
2.2.4 HISTSIZE环境变量
我们在xshell命令行上下翻,就能查看历史命令,其实是bash会将我们历史输入的指令给记录下来,并不能记录太多,所以设置了HISTSIZE环境变量=1000,只能记录最近的1000条命令。并且使用history就会得到历史输入的命令。如下所示:

2.2.5 PWD环境变量
PWD环境变量会记录当前的路径,如下所示:

当我们执行pwd指令的时候,就会输出PWD环境变量的值。
2.2.6 HOSTNAME环境变量
HOSTNAME环境变量会记录登录的主机名,如下所示:
![]()
2.2.7 OLDPWD环境变量
OLDPWD环境变量会记录前一个路径,每次我们使用cd -的时候,都会回到OLDPWD的路径,如下所示:
2.3 总结
- 环境变量(environmentvariables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,被bash使用,
- 如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
- 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性
- PATH:命令搜索路径,执行程序时依次在其内目录查找可执行文件;临时添加
PATH=$PATH:路径,仅当前终端有效。 - HOME:当前用户家目录,
cd ~依赖此变量。 - USER/LOGNAME:USER 为当前操作用户,LOGNAME 为初始登录用户;
su -会刷新二者,su不改变。 - SHELL:当前使用的解释器路径,默认 /bin/bash。
- PWD:当前工作目录,pwd 命令读取该值。
- OLDPWD:上一次工作目录,
cd -跳转依靠它。 - HISTSIZE:shell 保存历史命令最大条数。
- HOSTNAME:本机主机名。
- 编译相关
- LIBRARY_PATH:编译找库
- LD_LIBRARY_PATH:运行加载动态库
- C_INCLUDE_PATH:编译器查找头文件
基础特性
格式:变量名=值;终端直接赋值为临时变量,重启终端失效;子进程会继承环境变量。
查看命令
env查看全部,echo $变量名单独查看指定变量。
三、获取环境变量方法
- echo: 显示某个环境变量值
- export: 设置一个新的环境变量
- env: 显示所有环境变量
- unset: 清除环境变量
- set: 显示本地定义的shell变量和环境变量
3.1 如何增加一个环境变量
在上面我们是修改现有的环境变量,如果我们想要增加一个环境变量呢?
我们可以使用export命令,如下所示:

3.2 如何删除一个环境变量
可以使用unset命令,如下所示:
![]()
3.3 在代码中获取环境变量
在上面我们都是使用命令的方式获取环境变量,如果我们想要在我们写的c语言代码中获取环境变量,那应该怎么做呢?
3.3.1 方法一:使用env参数
我们回到1.2部分中,我们说了main是有参数的!!!main可以有0个参数或者2个参数,或者3个参数!!
并且通过代码介绍了main函数有两个参数的例子,其实main参数可以有三个参数,那么这三个参数分别是什么呢?
答案是int类型的argc,char*类型的argv和char* 类型的env。在上面我们介绍了int类型的argc,char*类型的argv,接下来我们来介绍env。
在main函数中,谁调用main函数,就会传入3个变量,分别是int类型的argc,char*类型的argv和char* 类型的env。一般都是父进程传入这三个变量给子进程的。
我们写的代码编译成为可执行程序,然后运行,它的父进程其实是bash,在上面说过,bash进程本来就有两张表,一个叫命令行参数表,一个叫环境变量表。bash会做命令行解析,并且将解析好的命令通过命令行参数表传递给子进程,环境变量表在登录的时候就创建好了,也可以叫环境变量表传递给子进程。
测试代码如下:
#include <stdio.h>
int main(int argc,char* argv[],char* env[])
{
(void)argc;
(void)argv;
for(int i=0;env[i];i++)
{
printf("env[%d]->%s\n",i,env[i]);
}
return 0;
}
为什么我们需要加这两行代码嘛?(void)argc;(void)argv;
因为,我们在main函数中定义了它们,但是没有使用,编译会弹出警告,所以使用这种方式使用一下,让编译器不弹出警告!
运行如下所示:

那么这个环境变量是从哪里获取的呢?
答案是从父进程那里获取的,也就是bash,环境变量可以被子进程继承,也可以被子进程的子进程继承,因此,bash进程下面的所有进程都能够得到环境变量,所以环境变量是具有全局特性的,如果我们在当前bash进程增加几个环境变量,然后再次运行代码,运行结果也会发生变化,这也充分证明了main函数里面的env指针数组是从父进程那里获取的。如下所示:

3.3.2 方法二:使用getenv函数(推荐)
我们使用man来查看一下getenv的使用,如下所示:

他的作用是获得指定环境变量的地址,如果没有返回null。
测试代码如下:
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char* argv[],char* env[])
{
(void)argc;
(void)argv;
(void)env;
char* value=getenv("PATH");
if(value==NULL) return 1;
else
{
printf("PATH->%s\n",value);
}
return 0;
}
编译运行如下所示:

现在我想让我写的代码只允许我一个人用,其他的人都不能用,那我们应该如何写代码呢?
在当前系统中,我们只有bash知道当前用户是谁,这是因为bash中记录了用户是谁,
代码如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char* argv[],char* env[])
{
(void)argc;
(void)argv;
(void)env;
const char* who=getenv("USER");
if(who==NULL)
{
return 1;
}
if(strcmp(who,"zhang")==0)
{
printf("这是程序的正常逻辑\n");
}else
{
printf("only zhang\n");
}
return 0;
}
运行如下所示:

为什么环境变量要被子进程继承呢?
因为我们把环境变量继承下去,子进程就可以结合环境变量来做个性化操作。
如果子进程想要修改,会发生写时拷贝的。
3.3.3 方法三:使用environ全局变量
我们使用man来查看一下environ的使用,如下所示:

environ的类型是一个二级指针,这是因为它指向环境变量表,环境变量表里面存放的是char*类型的指针,所以environ的类型是char**。
测试代码如下:
#include <stdio.h>
#include <unistd.h>
extern char** environ;
int main()
{
for(int i=0;environ[i];i++)
{
printf("environ[%d]->%s\n",i,environ[i]);
}
return 0;
}
编译运行如下所示:

四、理解环境变量的特性
在上面我们说了环境变量具有全局属性,在子进程里面也可以使用。
我们还可以定义本地变量,如下所示:

可以使用set命令来查看本地变量和环境变量,这个本地变量不会被子进程继承,只在bash内部使用。使用env只能查看环境变量,不能查看本地变量
我们定义了本地变量i之后,还可以使用export i 将本地变量导成环境变量,如下所示:

我们使用export导入环境变量,这个环境变量是导入到谁里面了?
环境变量是被导入到bash里面了,bash的环境变量可以被子进程来继承,但是我们使用export来导入环境变量,本质上不是应该是bash创建了一个export的子进程,这个子进程来导入环境变量嘛?但是子进程是如何将变量导入到bash进程里面,放到bash进程的环境变量表中呢?进程之间是有独立性的,父进程可以将环境变量给子进程,子进程是不能将数据给父进程的。所以export这种命令一旦被执行,怎么可能将环境变量给bash呢?
答案是:export这种命令和其他的命令不一样,export叫做内建命令,这个命令在执行的时候不需要创建子进程!!而是由bash自己去执行。
五、总结
5.1 环境变量概念总结
- 环境变量(environmentvariables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,被bash使用,
- 如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
- 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性
5.2 具体的环境变量总结
- PATH:命令搜索路径,执行程序时依次在其内目录查找可执行文件;临时添加
PATH=$PATH:路径,仅当前终端有效。 - HOME:当前用户家目录,
cd ~依赖此变量。 - USER/LOGNAME:USER 为当前操作用户,LOGNAME 为初始登录用户;
su -会刷新二者,su不改变。 - SHELL:当前使用的解释器路径,默认 /bin/bash。
- PWD:当前工作目录,pwd 命令读取该值。
- OLDPWD:上一次工作目录,
cd -跳转依靠它。 - HISTSIZE:shell 保存历史命令最大条数。
- HOSTNAME:本机主机名。
- 编译相关
- LIBRARY_PATH:编译找库
- LD_LIBRARY_PATH:运行加载动态库
- C_INCLUDE_PATH:编译器查找头文件
5.3 命令总结
- echo: 显示某个环境变量值
- export: 设置一个新的环境变量
- env: 显示所有环境变量
- unset: 清除环境变量
- set: 显示本地定义的shell变量和环境变量
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐






所有评论(0)