基本理解

环境变量⼀般是指在操作系统中用来指定操作系统运行环境的⼀些参数

环境变量可以理解为 “系统或程序运行时用的‘隐藏小助手’”,它们像标签一样贴在系统或程序背后,默默提供关键信息,让程序能正确运行。

例如你刚搬到一个新城市,需要快速适应生活。你会在手机里存些重要信息:

  • 超市地址(相当于PASH):方便你随时找到最近的超市买日用品。

  • 默认快递地址(相当于HOME):别人寄东西时直接填这个地址,不用每次重复。

环境变量就是这类“系统级的快捷信息”,程序或系统需要它们时直接调用,不用每次都手动输入。

再例如

1.你在终端输入c++命令,但系统不知道c++安装在哪里,会报错“命令未找到”。而通过PATH环境变量告诉系统:“C++在/usr/bin/cpp 这个目录下”,这样这个命令就不需要指定路径可以直接执行。

2.程序需要保存文件,但不知道存到哪里,可能乱存到系统目录。

而HOME这个环境变量指向你的用户目录(如/home/yourname),程序会自动把文件存到你的“家目录”里。类似你快递寄存地点为“XX小区X栋X室”,这那该地址设置为默认地址之后,以后就不需要每次都告诉快递员你的地址。

命令行参数

之前的学习中我们找到 main是程序的入口,但其实main函数不是第一个被执行的函数,它也是被其他的函数所调用的,_start是程序真正的入口点,main通常由_start间接调用,但具体实现与具体的操作系统与编译器有关。

所以,main函数里面也可以有参数的,只不过这个参数是可写可不写的。

只不过命令行参数并非所有程序都需要,如果程序不需要参数,强制要求参数会增加代码复杂度。

如下,main函数第一个参数argc是一个int类型的变量,第二个参数argc是一个char*类型的指针数组。 如果我们把argc里面的内容都打印出来可以发现打印的结果是我们的程序名(./code)。

而如果我们打印的时候像指令一样带参数的话 对应的参数也会被打印出来

所以命令行输入的参数会以空格为分隔 把输入的内容划分为多份分别放入argv这个char*指针数组当中,而第一个参数argc统计的就是里面元素的个数,在argv数组中需要以NULL为结尾。

假设我们期望我们的code程序的使用方式为 code [-a/-b/-c] (只能带一个参数 -a/-b/-c)

 其他方式都是错误的,使用错误就会告诉使用者正确的使用方法。

如下我们根据argv及argc的数据就能设计上面这样的code程序。

main的命令行参数 是实现程序不同子功能的方法

我们也就可以理解了 指令为什么通过不同的参数就可以执行不同的运行结果。

具体环境变量PASH

自己的程序与指令的区别

既然指令就是按照这样的方式来实现的,但是在使用上还是和我们有差别:

我们执行我们自己写的code程序的时候需要用 ./code的方式来执行(通过路径的方式找到当前目录下的code程序)

而我们执行指令的时候不管在哪个目录下却可以直接ls pwd使用 却不用带他们的路径。

而如果把我们自己的程序拷贝到 /usr/bin路径下(需要root权限),就会发现我们自己的程序执行时候不需要带路径了就像指令一样可以直接执行 并且像指令一样在任何目录下都可以直接执行!

那么上面这样的情况是为什么呢?

其实这就是因为系统当中存在环境变量的原因!

执行程序需要带路径,而自带的指令在 /usr/bin路径下,通过环境变量PATH可以直接找到,所以把我们自己的程序放到这个路径下也可以不带路径了。

PATH 系统中搜索指令的默认路径

查看环境变量

env    查看所有环境变量        环境变量包含 名字+内容  

如下通过env可以看到里面有很多的环境变量

环境变量 显示的方式是等号左边是名字,等号右边是内容。

echo $具体环境变量名字    查看具体的环境变量内容

如上面查看PASH环境变量的内容,看到里面的内容是多个路径 ,路径之间以冒号为分隔,划分成了多条路径 。

而我们在执行一个命令的时候如果没有手动指定路径就会优先在PASH第一条路径下找如果没找到就看下一个路径,以此类推,如果所有路径找完了还没有找到,就会报not found这样的错误 。

所以这就能解释了上面的现象(code不在/usr/bin路径下直接使用会报not found,而把code放到/usr/bin路径下直接使用可以正常执行)就是因为/usr/bin路径在PAH环境变量中!

既然code程序放到/usr/bin路径下可以直接使用 是因为该路径在PATH环境变量里面

那么如果把code所在的路径直接放到这个PASH环境变量里面是不是code也就能直接执行了? 

是的,接下来就来测试一下。

给PASH环境变量添加新路径

添加方式 PATH=$PATH:新添加的路径

$PATH就是原有的PASH里面的路径,如果不加$PATH,直接PATH=新添加的路径,那么原有的PATH下的路径就没有,此时ls这样指令就不能直接执行了(但是pwd、cd这些能执行 因为他们是最后提到的内建命令),当然如果重启一下Xshell后,PATH会恢复原有的路径。

如果理解环境变量(存储角度)? 环境变量从哪里来?

之前学习我们知道当一个用户登陆的时候,操作系统会给我们创建一个bash进程。

bash就会从系统当中读取所有的环境变量信息,然后再bash进程内部形成一张表---环境变量表 。

所以 结合命令行参数表和环境变量表 ,此时我们执行 code -a这样的命令的时候(没有手动带路径)
1.会先以空格为划分把输入的code -a分为两块(code 和 -a),分别放到argv中的对应位置中。

2.然后把argv[0](也就是程序名称)在环境变量表中的PATH路径中,看这个程序名称在不在环境变量的目录当中。
如果在就创建子进程然后执行它,如果不在报not found。

环境变量最初在哪? 

环境变量内容存储在bash进程中,而bash进程是在登陆用户的时候系统分配的,那如果不登录用户,不就没有环境变量?环境变量最初在哪?

环境变量最开始是从系统相关配置文件中来的,bash进程创建的时候会从系统配置文件中读取环境变量,然后加载到自己进程当中。

如下,系统给每个用户的家目录下存在些bash隐藏文件,/.bash_profile、/.bashrcz这两个用户级配置最终会调用到系统级配置/etc/bashrc来加载环境变量。

总结

所以一个bash进程会有两张表(命令行参数表 环境变量表) 通过动态申请。

        而之前的学习中我们知道了一个用户登陆就会被系统分配一个bash进程,那么如果有10个用户登陆就会被分别10个bash进程,而每一个bash进程会有这两张表命令行参数表来读取命令行的各种参数,而环境变量表在进程被创建的时候就会从系统的配置文件中读取环境变量填充到自己的环境变量表当中

        这样一个命令行被执行的时候就会优先通过空格把输入的命令划分为多块内容,然后放到命令行参数表当中,而argv[0]就是程序的名称,如果该程序没有自己带路径,就会通过程序名称在环境变量表中的PASH来看里面的路径有没有该程序,有就执行,没有就提示not found 

bash进程退出的时候会把两张表给释放,但是环境变量还存在系统的配置文件当中,下次创建bash时候还可以重新加载。

更多的环境变量

之前指令部分我们知道cd ~可以直接到家目录下,其实就是通过环境变量HOME。

cd ~ 作为两个字符串到了命令行参数表,bash进程就知道我们执行的是cd命令并且参数是~,就把~替换成了HOME的内容。 

HOME  当前用户的家目录


 

SHELL 显示当前用户的默认 Shell 路径

USER 当前用户是谁

LOGNAME 当前登陆用户是谁

su和su -的区别

之前很常用的命令 (ctrl+r、上下键、!v) 这些能使用就是因为bash把我们历史的命令记录了下来,而系统不可能把我们所有的几万几十万条命令都记录下来,只会记录最新的1000条 这个数字就是HISTSIZE里面记录的数字  。

HISTSIZE 历史命令存储条数

history该指令就会打印下面历史的指令

HOSTNAME 打印当前主机名

LS_COLORS 配色的信息      例如我们有的文件内容打印出来时彩色的

PWD 当前shell所在的路径

  OLDPWD 上一次的路径          所以cd -可以回到上一次的路径下,就类似于cd~回到家目录

获取环境变量的方法

上面我们提到了下面这两种

env 查看所有环境变量

echo $环境变量名称 查看具体的环境变量

自己导入/去除环境变量

export  自己导入的环境变量

unset  去除的环境变量

代码方式获取环境变量的三种方式

1.main参数

开始的命令行参数那里我们知道了在main函数中可以有两个参数,其实main函数里面还可以有第三个参数---char* env[] 它就是环境变量表,是通过父进程bash创建子进程时继承而来的 (之后学习会详细了解到)。

父进程bash在创建的时候就会通过配置文件把系统的环境变量加载到自己的进程当中,而这里的子进程完全就是从父亲那里继承而来的,如果通过export往父bash里面增环境变量或者去环境变量,子进程得到的是完全一模一样的。 

而如果此时子进程再有一个自己的子进程,这个孙进程仍然会继续继承它父进程的环境变量 所以环境变量具有全局特性

那么main函数既可以没有参数有可以有一个两个或者三个参数 是怎么做到的呢

在命令行参数中提到main函数不是第一个被执行的函数,在linux中第一个被执行的是_start,main函数被_start执行,而_start会首先知道main函数里面写了几个参数,然后就会调用对应不同个数参数的main函数。

2.系统调用 getenv

getenv    通过环境变量名字获取该环境变量的内容  获取成功返回环境变量名字对应的内容,没找到返回NULL 。

所以基于此我们就可以设计一个程序,只有指定的用户才能执行的代码即使root账户也不可以。

我们知道bash可以获取环境变量USER(当前用户是谁),子进程会继承得到父进程的USER 然后通过getenv来获取对应的USER,看其内容是否为yx,只要是yx才会执行,否组不能执行。

3.environ

 

environ是一个的全局变量,它存储了当前进程的所有环境变量,指向环境变量表 ,是一个二级指针,因为环境变量表里面存的是一级指针。

推荐第二种方,因为第一第三组通常都是访问所有的环境变量,而第二种可以访问指定的环境变量

理解环境变量的特性

1. 环境变量具有全局性

这在上面的获取环境变量的main参数方式中也提到过,因为进程会继承父进程的环境变量

2.本地变量

如下,我们直接在命令行输入 i=10 ,此时用类似打印具体环境变量方式那样打印发现也可以打印出它,而不能用env来查看,因为它不是环境变量,可以通过set来找到,set 显示环境变量+本地变量bash会记录这两种变量。 

下面是两个本地变量 PS1的前面就是命令行的格式  PS2可以实现一行的内容分行来写

本地变量与环境变量区别在与不会被子进程继承 只在bash内部被使用

 unset i   取消本地变量  此时export i  把i直接从本地变量变为环境变量

这个环境变量是bash的,这个i=10的环境变量是通过export而导入的,但是export是bash的子进程 那这不就是子进程把数据放到父进程环境变量表中了?但是进程不是直接具有独立性吗?父进程可以把数据给子进程,而子进程是不能把数据给父进程的。那它怎么做到的?

因为export是内建命令,不需要创建子进程,而是让bash自己亲自执行。 

而之前环境变量PASH在被新路径替换掉原有的所有路径后,ls这样的命令就不能执行了,而cd、pwd这些还能执行就是因为他们也是内建命令,只要bash能运行,他们就能运行。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐