Hello,我是云边有个稻草人---csdn个人主页,与你分享Linux领域专业知识,今天继续学习环境变量。
《Linux系统》本篇文章所属专栏---持续更新中---欢迎订阅!
目录
正文开始------
【进程概念】相关知识回顾:
【Linux系统】第八节---进程概念(上)---冯诺依曼体系结构+操作系统+进程及进程状态+僵尸进程---详解!-CSDN博客
四、环境变量
1、概念介绍
- 环境变量(environment variables)⼀般是指在操作系统中⽤来指定操作系统运⾏环境的⼀些参数
- 如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪⾥,但是照样可以链接成功,⽣成可执⾏程序,原因就是有相关环境变量帮助编译器进行查找。
- 环境变量通常具有某些特殊⽤途,在系统当中通常具有全局特性
首先了解什么是命令行参数
【命令行参数】

2、一个例子,一个环境变量
【引入环境变量--->PATH】
为什么执行我们自己的的时候要带路径**./** ,而执行系统命令的时候就不需要带**./** ?因为我们创建的程序在当前路径下,而系统命令有环境变量来帮助系统找到对应的命令。
2.1 要执行一个程序,必须先找到它!
2.2 系统中存在环境变量PATH(系统中搜索指令的默认搜索路径),来帮助系统找到目标二进制文件!
/usr/bin 是 Linux 里绝大多数用户日常命令和应用程序的存放地,全是可执行的二进制文件,所有用户都能运行。
是不是将我们自己创建的命令放到 /usr/bin 路径下就可以直接执行了呢?是的,放到这个路径下面系统可以自动就能找到。强烈不建议将我们自己的命令放到该路径下,执行rm /usr/bin/名字 删掉。

为什么系统知道从这个路径下面找到对应的二进制文件呢? 因为环境变量 PATH(系统中搜索指令的默认搜索路径)!PATH下面有/usr/bin 路径,见下:
**env:打印当前系统所有环境变量,格式:名称=内容。**我们使用env确实查到了环境变量PATH,见下:

查看环境变量中具体某一个环境变量:echo $PATH

所以当我们执行 ls 命令时,系统会首先到环境变量PATH下找,按顺序在每个目录下匹配名为ls的可执行文件,找到首个匹配文件就加载运行,遍历全部路径仍未找到,提示 command not found。
那是不是将我们自己放命令的路径放到PATH里面是不是系统就可以直接找到我们自己创建的路径呢?是的!
PATH=$PATH:自己存放命令的路径

此时执行自己的程序就可以不带路径了。当我们重新启动Xshell后PATH里面的内容被重置。
【如何理解环境变量呢?存储的角度】
是存储在bash进程的上下文里面的

所以bash内部有两张表!命令行参数表,环境变量表。
但是,如果无人登录,那么环境变量还有没有呢?环境变量在哪里呢?
【环境变量,最开始是从哪里来的?】
系统的相关配置文件里来的!Bash 启动时读取配置文件,通过 malloc 在自身进程内构建环境变量表。

最后,如果Linux系统有10用户登录呢?存在10个bash。
我们开头讲的,执行一个程序,我们必须先找到它,谁找?bash,通过PATH来找(环境变量)。
可以自己梳理一下整个的动态过程。

3、认识更多的环境变量
HOME:指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)

如何生成的
用户登录系统、启动 bash 时:系统读取用户信息 + bash 配置文件,自动在bash 进程上下文的环境变量表 中,写入 HOME=xxx 这一条环境变量。
核心作用
- 直接输入
cd回车,会自动进入HOME指向的家目录; ~波浪号,本质就是 HOME 变量的别名 ,~等价于 $HOME;- 各类软件、配置文件(.bashrc、.bash_profile)默认都存放于 HOME 目录下
SHELL:当前Shell,它的值通常是/bin/bash。
bash = 主流默认 Shell,是 CentOS、Ubuntu 等系统默认使用的命令解释器。
记录当前终端使用的命令解释器类型。

- USER
- 含义 :记录当前登录操作的用户名。
- 特点 :切换用户后会同步变化,标识当前正在使用终端的用户。
- LOGNAME
- 含义 :记录最初登录系统的原始用户名。
- 特点 :一旦登录就固定不变,就算用
su切换别的用户,LOGNAME依然保留最开始登陆的账号。
su 用户名临时切换;
su - 用户名完整登录并加载新用户环境配置;

HISTSIZE:
限制当前终端会话(内存中) 能保存的历史命令最大条数。
bash
history # 查看所有历史命令
history 10 # 只看最近10条
!数字 # 执行对应编号的历史命令
!! # 重复执行上一条命令
history | wc -l #用于统计历史命令总行数,通过管道将 history 输出交给 wc -l 计数,结果受 HISTSIZE 限制。

hostname # 查看主机名
hostname 新名字 # 临时修改主机名
SSH_CLIENT=客户端IP 客户端端口 服务器SSH端口
SSH_TTY=/dev/pts/0:标记当前远程登录所使用的终端设备
PWD:记录当前工作目录绝对路径,
cd切换目录会自动更新,存放于 Bash 进程上下文,pwd命令直接读取该变量。OLDPWD:记录上一次所在的工作目录绝对路径,由 bash 自动维护,存放在进程上下文的环境变量表。
- 执行
cd 切换目录时:原来的PWD会自动保存为OLDPWD,再更新新的PWD- 命令
cd -本质:直接跳转至$OLDPWD目录cd / cd ~ 依赖环境变量 HOME ,作用:直接进入当前用户家目录 ,二者完全等价。
cd - 依赖环境变量 OLDPWD ,作用:快速切换到上一次的工作目录。
4、获取环境变量的方法
【命令操作】
env、echo $XXX。
export :设置⼀个新的环境变量。用于将变量导出到 bash 进程的环境变量表,使其能被子进程继承使用。
unset:删除变量,清空环境变量表中指定内容;与 export 相反,一个导出、一个销毁。

【通过代码如何获取环境变量】
方法一:获取环境变量,命令行第三个参数
理解一下:
bash
#include <stdio.h>
// main 函数的第三个参数 env[]
int main(int argc, char *argv[], char *env[])
{
int i = 0;
// 循环遍历 env 数组,直到遇到 NULL(结束标记)
for(; env[i]; i++){
printf("%s\n", env[i]);
}
return 0;
}
编译运行后,会打印出当前进程所有的环境变量,比如:
bash
PATH=/usr/local/bin:/usr/bin:/bin
HOME=/home/yourname
USER=yourname
SHELL=/bin/bash
SSH_CLIENT=...
上面代码里面的 env 是一个 字符串指针数组 ,它的每一个元素,都指向一个形如 KEY=VALUE 的环境变量字符串,数组的末尾以 NULL 作为结束标记。
当你在 bash 中运行这个程序时,bash 会把自己进程上下文里的环境变量表 ,完整复制一份,传给这个程序的 env[] 参数。这就是为什么程序能读到 PATH、HOME、SSH_CLIENT 这些你在 bash 里看到的变量 ------ 它们都是父进程(bash)传给子进程的。
env 数组的最后一个元素是 NULL,当 env[i] 为 NULL 时,条件不成立,循环自动结束。这是 C 语言中遍历以 NULL 结尾的数组的经典写法。
刚才用 export 往bash的环境变量里面导入几个环境变量,那子进程是否能继承我刚才添加的环境变量呢?可以!可以自己使用上面的代码验证一下。
所以,环境变量可以被子进程继承!环境变量是 "父传子、子传孙" 的链式继承关系,每一层子进程都会复制上一层父进程的环境变量,因此可以一直传递下去。所以环境变量在系统当中通常具有全局特性。
虽然变量会层层继承,但每个进程的环境变量都是独立副本,互不影响:
- 你在子进程 B 里修改 / 删除
MY_VAR,只会影响 B 和它的子进程 C,不会回传给父进程 A。- 这是典型的 ** 写时复制(Copy-on-Write)** 机制,父进程和子进程的环境变量是完全独立的。
方法二:getenv,根据环境变量的名字来获取环境变量的内容, 找到就返回值的字符串指针 ,找不到返回 NULL。



一个有意思的问题,如何使用 getenv 设计一个程序,只能我执行,其他人一律不执行?使用USER!


重新登录一个账号root,发现即使是root也不可以执行该程序正常的执行逻辑

方法三:通过第三⽅变量environ获取
environ 是 Linux C 语言的全局变量 ,它直接指向当前进程的整个环境变量表 。它就是 main 函数第三个参数 env [] 的真身。

每个程序都会收到⼀张环境表,环境表是⼀个字符指针数组,每个指针指向⼀个以'\0'结尾的环境 字符串
libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头⽂件中,所以在使⽤时要⽤ extern声明。

程序运行结果:

推荐第二种,获取具体某一个环境变量 getenv
5、理解环境变量的特性
5.1 环境变量具有全局特性(见上)
5.2 补充两个概念(给后面埋一个伏笔)
a、本地变量
i=10,是设置一个本地变量的操作

为什么 echo $i 能看到,但 env 看不到?
你定义的
i=10是「局部变量」
- 直接赋值
i=10(等号两边不能有空格),创建的是 Shell 局部变量- 局部变量只存在于当前 Shell 进程内部,只能用
echo $变量名查看env命令默认只显示环境变量,所以看不到它
env看不到的根本原因:它没被放进「环境变量表」
- 只有用
export导出的变量,才会被写入当前 Shell 的环境变量表env命令读取的,正是这张表,所以export过的变量才会被列出来set:显示环境变量和本地变量
关于本地变量
- bash会记录两套变量:1、环境变量,2、本地变量
- 本地变量不会被子进程继承,只会在bash内部被使用!
- 本地变量就是当前 Shell 的 "私有临时变量",适合做临时数据暂存、脚本内逻辑控制,轻量又安全,不会污染全局环境和子进程。
export 变量名,是将已存在的本地变量 ,升级为环境变量,供子进程继承。

b、首先我们要知道:我们的环境变量是在谁里面?bash!
所以,有个问题:export 会将环境变量导入到父进程 Bash 中,但如果 export 是作为子进程执行的,那么按照进程独立性原则,子进程的修改不应该影响父进程,但实际上 export 确实改变了 Bash 的环境变量,这是为什么呢?
因为export是一个内建命令built-in command,内建命令--->不需要创建子进程,而是让bash亲自执行,bash自己调用函数,或者系统调用完成。pwd 也是 Bash 内建命令,只要当前 Bash 进程正常运行,就能执行 pwd 命令获取当前工作目录,不依赖外部可执行文件。
结束,现在再回看一下环境变量的概念就会好理解一点。
- 环境变量(environment variables)⼀般是指在操作系统中⽤来指定操作系统运⾏环境的⼀些参数。
- 如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪⾥,但是照样可以链接成功⽣成可执⾏程序,原因就是有相关环境变量帮助编译器gcc/g++进行查找。
- 环境变量通常具有某些特殊⽤途,在系统当中通常具有全局特性。
完------

至此结束------
我是云边有个稻草人
期待与你的下一次相遇!