目录
PATH环境变量
平时使用指令的时候,像 ls、pwd、cd这些直接用就可以执行了,但比如我们自己写的可执行程序不能直接运行,要在前面加上路径 ./
由此可知,运行一个指令(程序)需要先找到它的位置,上面运行mytest在前面加上 ./ 就是提供当前路径,让系统知道要执行当前路径下的这个程序。
那如果我想让我的程序也像系统指令一样不用带路径直接运行该怎么办呢?
————sudo cp mytest /user/bin 将我们的程序拷贝到系统路径下,这样运行的时候系统就能直接找到该程序运行:
如此一来就能直接运行了。但是不推荐这种用法,因为我们写的程序没经过测试,并不安全,很容易污染系统指令池。
系统指令不需要路径寻找与环境变量有关,Linux下有个环境变量叫PATH,它是在操作系统启动时根据shell上下文定义的变量,具有全局属性。
想查看 echo $PATH
操作系统搜索指令时,先去环境变量下这些对应目录查找(目录与目录间用 :分割)找不到就报commend not found。
想让我们的程序和系统指令一样,可以将程序添加到环境变量下:
但是注意不能如上操作,这样会将原本的环境变量全部覆盖,系统指令就无法使用了。万一误操作覆盖了直接退出操作系统重新登录就能恢复了,因为这是内存级环境变量。
所以正确用法是先添加系统路径,再添加自己的程序路径:
常见环境变量
除了PATH我们还有很多环境变量。
我们很多指令之所以能执行,也是依据对应环境变量。
$HISTSIZE 可以记录历史上的3000条指令,history就可以查看这些历史记录L:
history | wc -l 统计历史指令数。
还有一个env可以查看所有环境变量:
USER环境变量
USER环境变量可以显示当前登录用户。它与权限有着密切关系。比如我们想进入其他用户的家目录,而他设置了other访问权限让其他人无法访问,那操作系统是怎么识别我们当前是谁来匹配对应身份信息的,就是这里的USER环境变量。
现在我不想通过系统指令来获取环境变量,我想通过函数调用来获取环境变量,可以使用getenv :
getenv函数的参数是环境变量的名字,返回值是环境变量的内容,环境变量用了语言上变量的概念,USER=bc有等号,左边是环境变量名右边是内容。
我们自己来实现一下USER指令:
当前我是bc,我再切成root运行一下该程序:
现在我被标识成了root。
所以USER环境变量的意义是:标识当前使用Linux的用户。
再来看,我现在改一下代码,如果我是root用户就执行打印用户操作;如果不是root,打印“权限不足”。
这里就演示了上面说的Linux下用户权限匹配机制,根据USER 环境变量来识别用户身份,再匹配权限。
环境变量相关命令
除了环境变量,Linux下也可以用户自己定义变量。环境变量可以理解为全局的,自己定义的变量没有导入到环境变量就是本地的、局部的。
用户创建一个本地变量myval,echo $myval可以查看到,但是在环境变量中没有myval。
举个例子,刚刚我们用getenv获取系统环境变量,现在也可以用它获取自己导入的环境变量:
当该变量是环境变量时打印出环境变量内容,不是环境变量打印not found。
如果变量是本地变量,用env查看全局变量不会有,想将本地变量变成环境变量可以用export导入:
也可以在导入时直接在变量后跟数值:
如果想要移除导入的环境变量,用unset命令:
提到unset,还有个命令叫set, 它可以查看所有本地变量+环境变量。
要移除也是一样unset。
然后再讲一下环境变量的全局属性。
当我们运行一个可执行程序。它变成一个进程,那么它是谁的子进程呢?是命令行解释器bash的,bash是一个系统进程。环境变量具有全局属性,会被子进程继承下去,也就是说本来环境变量是定义给系统的,随着bash继承给子进程,子进程也具有了环境变量,所以说具有全局性。
那为什么要继承环境变量呢?————因为系统中很多应用场景需要环境变量,比如上面说的权限访问,识别用户....都需要环境变量。
环境变量的意义
其实根据上面的理解我们已经大致知道了环境变量的意义,下面我再举个例子:
上面说了ls 可以直接使用不需要路径是因为环境变量里面有系统指令的路径,那么这里ls makefile
makefile不在环境变量里,为什么不用加路径就能直接执行呢?按理说不应该是ls ./makefile吗?
———有人会说makefile是当前路径下的文件,所以不用加路径。那 ls 是怎么知道的?——因为还有一个环境变量在起作用:PWD
该环境变量里就存了对应路径,所以我们可以直接使用。
如果我们回退上级路径,环境变量PWD也会随之改变:
所以环境变量在系统中起到了相当大的作用 ,而且环境变量非常多,能应用于许多场景。像我们之前以为复杂的指令,比如hostname获取主机名,其实就是简简单单用C函数调用一下环境变量HOSTNAME就打印出来了。很多指令都是调用环境变量,调用系统接口用C/C++语言实现的。
命令行参数
在学习C语言的时候使用main函数,main函数就带有3个命令行参数,只是我们一般不用:
int main(int argc,char* argv[],char* env[])
这里的argv是指针数组,argc是指针数组的个数。env先不管。
那这和Linux有什么关系呢?
我们在使用指令的时候经常会带指令选项,同一个指令带不同的选项效果也不同:
ls -l , ls -a , ls -d ...
这种效果是怎么产生的,就和命令行参数有关。
程序每多带一个选项,argv数组里就会多一个char*变量,argc+1。
命令行参数,实际上是要把程序名(./mycmd)和选项(-a -b -c)依次传给argv的。
传给argv的过程,会按空格将命令拆分,./mycmd -a -b -c 就会拆成4个字符串:"./mycmd"
"-a" "-b" "-c" ,然后再放入argv指针数组中。
这里给argv数组传参还有解析字符串是由系统和shell一起完成的。
argc就是参数个数,argv就是一张映射表存参数。
下面我们来模拟实现一下一个指令的选项是如何做成的。
通过对指针数组里的字符串和选项字符串比较,使用对应功能,可以任意组合选项。如果使用指令选项不符合规范,就打印出指令及相关选项给用户参考。
所以同一个指令通过选项执行不同命令就是通过命令行参数实现的。
刚刚说了int main()还有一个参数char* env[ ] ,它和char* argv[ ] 一样是指针数组,存的是环境变量。这样进程就能拿到环境变量了,所以子进程可以继承bash的环境变量。
我们来验证一下:
for循环中结束条件是env[i] ,因为env没有argc那样记录个数的参数,而env数组的最后一个是NULL,当数组获取环境变量到最后一个是NULL也就结束了。
可以看到每个env数组里都存了环境变量。
如果现在我不想给int main()传参数,刚刚3个命令行参数都不用,还可以用一个新的东西来获取环境变量,它就是系统定义的二级指针extern char** environ。该指针是系统编译完成时默认创建好的一个全局二级指针,在C库中使用时要extern外部声明一下。
这种方式依然能获取环境变量。
总结一下:获取环境变量的方法有3种:
1、getenv() 参数是环境变量名,通过变量名就能拿到环境变量内容,而其他两种方法要对字符串解析才能拿到环境变量内容,所以推荐使用该方法。
2、char* env[]
3、extern char** environ