目录
背景
大家在学习Java或Pathon亦或安装某个软件时,大多经历过要修改环境变量的情况,那环境变量是什么?下面让我们一起来揭开环境变量 的神秘面纱。
1.概念
- 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数。
- 当我们在编写C/C++代码时,在链接时,我们从来不知道所链接的动态库在哪,但是照样可以链接成,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
- 环境变量通常具有某些特殊的用途,还有在系统当中通常具有全局特性。
2.常见环境变量
- PATH:定义解释器搜索用户只需命令的路径
- HOME:指定用户的主工作目录(即用户登录到Linux系统中时,默认的目录)
- SHELL:当前Shell,它的值通常是
/bin/bash
- USER:存放当前用户的用户名
通常我们使用
echo $NAME
来查看对应的环境变量内容(NAME:环境变量名)
- PATH中各路径使用冒号 : 分隔
2.1 PATH
首先,我们创建test.c 文件,编写如下代码
其次,创建makefile 文件,编写如下代码
当我们完成相应的操作,使用
make
指令创建对应的可执行文件myprocess ,当我们想要执行这个可执行文件需要使用
./myprocess
,那我们可不可以直接使用
myprcess
来执行呢?答案当然是不行,那要则没做才能办到呢?这就和环境变量有关了。
指令和自定义程序的区别
在Linux中,我们使用指令(如:ls)来操作系统,而指令也是使用C写的,放在系统的某个位置,和我们写的程序是没有区别的,这里我们使用
file
指令来查看
ls
和
myprocess
的区别
我们从上图可以看到,指令和我们写的程序是没有区别的,这些指令就是一个个已经写好的可执行文件。
我们通常想要执行一个可执行文件需要使用绝对路径或相对路径来定位需要执行文件的位置
- 相对路径:
./
,其中.
表示当前路径,/
为路径分隔符- 绝对路径:
home/../../可执行文件
所以,想要执行一个可执行文件,就需定位它的路径,而**我们使用的指令之所以不用带路径就能执行,是因为它被纳入到基本指令的范畴,路径已经实现被放到了环境变量
PATH
中**,如下图
每当我们使用指令完成某个操作,系统就会从左向右,找到环境变量中对应的路径,执行该指令的可执行文件。
了解了这些,我们就有两个方法使得我们的可执行文件,不使用路径直接运行。
- 将可执行文件放入已经在环境变量中的某个路径下(会有权限限制,需在root用户下执行或sudo提权)
- 将可执行文件对应的路径添加到PATH 环境变量中
这里我们只讲第二种方法。
向环境变量PATH中添加路径
使用下面的指令来完成路径的添加:
export PATH=$PATH:对应的路径
- 其中,是将
$PATH:对应的路径
赋值给PAT,新的值会覆盖原有的值,$PATH
代表PATH中的原路径,使用:
分割,之后是添加的新路径
如下图,向PATH环境变量 中添加myprocess 可执行文件的对应路径,之后查看PATH是否修改
此时我们就在该目录下创建的可执行文件,即可只使用文件名运行了
删除PATH中的路径
下面的指令表示为PATH重新赋值,将之前的值覆盖
export PATH=对应的路径
比方说,PATH中的路径如下:
路径1:路径2:路径3:路径4
我们想要删除路径4 ,只需复制路径1:路径2:路径3,之后复制给PATH即可
export PATH=路径1:路径2:路径3
我们使用该方法删除上面添加的路径,如下图
注意:
若使用
export PATH=..
时使用错误,覆盖了原来的所有路径,重启终端(关闭shell,重新启动)即可。
也就是说,我们进行PATH的配置只在本次登录有效,当重启后,我们添加的路径就会消失,变为原本系统默认的路径。如果想要可执行文件永久使用文件名运行,需要将其移至永久有效的路径下,相同的删除也只能删除路径下对应的文件。(拷贝和删除文件时有权限限制)
系统如何执行一条命令
学习Linux的人应该都知道,在Linux系统中一切皆文件 ,Linux命令(指令、运行文件等等)也不例外,那了解了上面
PATH
环境变量的知识后,那大家可能会好奇,当用户在Bash命令行解释器执行了一条命令后,Linux系统到底发生了什么事情?主要分为以下4个步骤:
- 判断用户是否已绝对路径或相对路径的方式输入命令,如果是,则直接执行。
- Linux系统会检查用户输入的命令是否为"别名命令",即用一个自定义的命令名称来替换原本的命令名称。 如:我们执行的
rm
指令其实就是Linux系统为了防止用户误删文件而特意设置的别名命令,操作系统真正执行的命令为rm -i
(-i:在删除文件时提示用户是否删除) -alias
:查看和替换命令原本的命令名称 - 命令行解释器判断用户输入的是内部命令还是外部命令。内部命令是解释器内部的指令,会被直接执行;而用户在绝大部分时间输入的是外部命令,这些命令由步骤4继续处理。 - 可以使用“type命令名称”来判断用户输入的命令是内部命令还是外部命令。
- 系统在PATH环境变量中查找用户输入的命令文件,找到命令所在的目录后运行该命令,否则告诉用户命令无法执行。
经典问题:
为什么不能将当前目录
.
添加到PATH中呢?
尽管可以将当前目录
.
添加到PATH变量中,从而在某些情况下可以让用户免去输入命令所在路径的麻烦。但是,如果黑客在比较常用的公共目录中存放了一个与
ls
或
cd
命令同名的木马文件,而用户又恰巧在公共命令中执行了这些命令,那电脑既有可能中招了。
所以,作为一名态度谨慎、有经验的程序员,在接手一台Linux系统后一定会在执行命令前先检查PATH变量中是否有可疑的目录。
2.2 env:显示所有环境变量
我们可以使用
env
指令,显示所有的环境变量,如下:
在Linux系统中,变量的名称一般都是大写的,这是一种约定俗成的规范。我们可以直接通过变量名称来提取到对应的变量值。
其中:
- HOSTNAME: 表示这台机器的主机名
- HISTSIZE: 表示保留之前指令的最大数量我们想要看到命令行中输入的历史命令可以使用
history
指令,但操作系统不会将我们所写的指令都保存下来,只会保留最新的HISTSIZE
个历史指令 - SSH_CLIENT: 表示当前是那台主机在登录我们的Luinux机器
- USER: 当前登录用户我们创建一个文件或是目录,创建后它的所属组和拥有者都是根据USER来给定的- 使用
su
指令切换用户,USER不会发生改变- 使用su -
指令切换用户,USER会发生改变 - LS_COLOR: 配色方案当我们使用某些指令时,会发现一些数据的颜色区别于其它数据,这就是由LS_COLOR指定的
- PWD: 确认当前用户所在的路径我们使用
pwd
指令获取当前所在位置时,使用的就是这个环境变量 - HOME: 表示当前用户所在的家目录不同的用户,HOME值不同
- LOGNAME: 表示登录的用户- 使用
su
指令切换用户,LOGNAME不会发生改变- 使用su -
指令切换用户,LOGNAME会发生改变
- 环境变量是根据特定的人在特定的场景中使用的
Linux 作为一个多用户多任务的操作系统,能够为每个用户提供独立的、合适的工作运行环境,因此,一个相同的变量会因为用户身份的不同所处位置不同而具有不同的值。
2.3 环境变量相关的命令
- echo: 显示某个环境变量值(在环境变量名前加$符)
- export: 设置一个新的环境变量
- env: 显示所有环境变量
- unset: 清除环境变量
- set: 显示本地定义的shell变量和环境变量
3.通过代码获取环境变量
C/C++代码,main函数是程序的入口,,main函数可以接收参数,最多可以接收三个或是两个参数,写法如下:
intmain(int argc,char* argv[],char* envp[]){}
- 编译器在编译时会自动传递这三个参数,只是我们不用而已
- 其中,也可以只接收前两个参数,最后一个不写,我们这里先来讲最后一个参数
char* enpv[]
,知道这个剩下两个就好理解了。 - 我们的进程内部本身就有环境变量,这需要我们去获取(下面就是获取方式)
1.char* envp[]
envp为指针数组,main函数的第三个参数,存放指针,指针对应的数据为环境变量(执行一个额一’\0’结尾的环境字符串),第一个无效内容指向NULL
这里我们要区分两个概念:
- 指针:表示地址,凡是具有指向能力的数据都叫指针
- 指针变量:表示变量,在内存中开辟空间,具有存储和被修改的能力
envp指针数组存放的是指针,指针的类型为char,指向字符串或字符,这里的envp是指向字符串,也就是环境变量。
我们将test.c 文件修改如下:
其中,当
envp[i]
为NULL时表示有效数据已经循环完毕
将makefile文件修改如下:
使用
make
指令生成可执行文件,之后运行,得出下面的结果
输出的正是环境变量,我们可以通过该方法在我们自己写的程序内获取环境变量。
2.第三方变量enciron
除了main函数的第三个参数,我们还可以使用libc中定义的全局变量environ 指向环境变量表,来获取环境变量。
注意:
- environ没有包含在如何头文件内,在使用时要用extern声明,表示为外部变量。
- environ为二级指针,相当于指针数组指向环境变量表
将test.c 修改如下:
使用make指令生成对应的可执行文件,之后执行该文件,获得如下结果
我们依然可以看到,使用该方法就可以获得环境变量。
问题:
我们已经知道可以通过main函数的第三个参数或二级指针environ变量获取环境变量,然后呢?难道我们要用的时候只能通过遍历环境变量表来获取想要的环境变量吗?这是不是太麻烦了?
这样做在我们想要获取某一环境变量时却是i不方便,所以,我们在实际操作中最常用的是下面要讲的函数获取环境变量,想要什么环境变量直接告诉函数,通过它来获取。
3.getenv函数获取指定环境变量
通过向getenv函数传递想要获得的环境变量的环境变量名来获取,该函数头文件为**<stdlib.h>**
格式:
char* 变量名 =getenv("环境变量名");
我们通过下面的代码来演示,获取USER的环境变量,其它的环境变量也是一样的
首先,将test.c 文件修改如下:
使用make指令生成可执行文件,执行该文件获取如下结果:
- 想要获取其它的环境变量也是使用相同的方法
4.利用获取的环境变量
现在我们既然可以获取到环境变量了,那我们该用它来做什么呢?下面通过几个例子来向大家介绍一二
自制pwd
我们可以自己写一个程序,来获取当前所处的位置,如下:
首先,再次将test.c 文件修改如下:
使用make指令生成可执行文件后执行它,获取如下结果:
我们还可以将该可执行文件的路径放在PATH环境变量下,在为其改名为mypwd ,这样我们在如何地方都可以执行它,如下:
限制文件执行对象
若一个可执行文件,我们只想要让自己或其它指定用户使用,我们可以依靠环境变量做出限制
首先,再次将test.c 文件修改如下:
注意:该程序用到strcmp函数,该函数需要引用string.h 库
其次,使用make指令生成可执行文件,在两个用户下执行该文件,查看结果
用户YX
root用户
- 注意:这里要使用
su -
来切换到root用户的家目录,在到可执行文件的目录下运行该文件,才可以看到效果。否则对应的USER环境变量不会改变
4.环境变量来自哪里
环境变量是我们登录LInux系统后,在对应用户的家目录 下的
.bash_profile
和
.bashrc
两个配置文件中获取的
.bash_profile
打开
.bash_profile
文件查看其内容
在我们登录成功后,系统会调用该配置文件,执行其内容,调用
.bashrc
配置文件,加载配置文件
PATH
环境变量得以添加。
.bashrc
在打开
.bashrc
文件查看其内容
/etc/bashrc
- 存放全局的环境变量
总结:
登录系统后,系统会调用对应用户家目录下的
.bash_profile
配置文件,
.bash_profile
配置文件中又调用同目录下
.bashrc
配置文件,而
.bashrc
配置文件又调用
/etc/bashrc
配置文件,生成环境变量。
5.环境变量的全局性
环境变量本质就是一个内存级的一张表,这张表由用户在登录系统时,进行给特定用户形成属于自己的环境变量表。而环境变量通常具有全局属性,可以被子进程继承下去。
我们先来看一下下面的两种情况:
情况1:
我们在命令行中编写一个变量,并为它赋值,之后使用
echo
指令取出
情况2:
在命令行编写变量并为它赋值时使用
export
指令修饰,使其变为环境变量,之后使用
env
指令查看
大家或许都知道这两种现象的存在,但对于它为什么存在却知之甚少,原因如下:
情况1
系统启动后,它的shell也是一个进程在内存内有自己的一块空间,用来存储环境变量表或是读取到的命令等等,当我们在命令行中写下字符串mynum=100 后,会在它的上下文中使用malloc 开辟一块空间,用来存放对应的字符串,如下图:
- 此时被shell存下的变量叫做本地变量,只在shell的内部有效
情况2:
当我们使用
export
指令后,系统会在环境变量表的第一个无效的内容处存放
export
后的字符串,或者可以理解为在环境变量表中使用malloc 开辟一块空间存放字符串
全局性
我们在Linux中使用指令或是执行可执行文件都是创建子进程的过程,而这些子进程的父进程都是shell,在子进程创建时,shell会将自己内部维护的环境变量表传给子进程,使其也可以使用环境变量,这就是环境变量通常具有全局属性,可以被子进程继承下去。
证明如下:
将test.c 文件修改如下:
使用
make
指令生成对应的可执行文件,并执行,结果如下:
通过上图,我们看到在命令行中我们设置的环境变量mynum=50 在子进程中依然可以得到,父进程的确将环境变量交给了子进程。
证明:环境变量是可以被子进程继承下去的!
6.环境变量总结
- 环境变量本质就是一个内存级的一张表,这张表由用户在登录系统时,进行给特定用户形成属于自己的环境变量表。
- 环境变量是由shell维护的,创建子进程后shell会使用一些方法将其传给子进程,证明环境变量具有全局属性。
- 环境变量中的每一个,都有自己的用途,有的是进行路径查找的,有的是进行身份认证的,有的是进行动态库查找的,有的是用来进行确认当前路径等等。
- 每个环境变量都有自己独特的应用场景。
- 每一个环境变量都有自己的名字和内容。
- 环境变量都是从相关配置文件中读取出来的(所以每次登录环境变量是相同的,在家目录下的
.bash_profile
和.bashrc
中)
7.main函数的前两个参数
main函数是可以接收三个参数的,最后一个是接收env环境变量表的,而前两个是可以单独写的,用来接收选项 。
intmain(int argc,char* argc[],char* envp[])
我们在执行指令时经常后待一些参数,比如使用
ls
指令时,会使用
-l、-a
等参数
而
ls
也是一个可执行文件,该文件所在位置存放在环境变量
PATH
中,我们可以不去管它的位置,直接使用文件名即可运行,既然是可执行文件,那它对应的程序中一定有main函数,它main函数中的前两个参数就是用来接收,命令行中输入的选项的。
intmain(int argc,char* argv[])
- argv:存放命令行中传入的选项的地址(包含可执行程序这个字符串),第一个无效字符存放NULL
- argc:表示传入的选项个数
接下来,我们来测试一下:
先将test.c 文件修改如下:
接着,使用
make
指令,生成可执行文件,执行如下指令
我们看到,父进程bash将命令行中的字符串使用空格分割,传给子进程main函数的argv 参数。
- 这就是我们在使用指令时,对应的程序识别选项的方法原因。
我们接下来写一个小程序,来模拟选项的实现:
将test.c 文件修改如下,功能为,只向可执行程序传输一个选项(不包含可执行文件名),执行对应的操作,若一个没有则返回一段提示,告诉用户需传输选项:
使用
make
指令,执行可执行文件,对其进行测试,如下:
版权归原作者 榶曲 所有, 如有侵权,请联系我们删除。