
前言
前面我们学习了进程、PCB、fork、进程状态、进程切换,我们知道 fork 创建子进程时,子进程会拷贝父进程的 PCB 与相关资源。 在 Linux 中执行ls、pwd、whoami这类命令,我们不需要输入完整路径/bin/ls,直接敲命令名就可以运行程序,这个能力的底层支撑就是环境变量。
环境变量是一组以key=value形式组织的全局字符串,存放在进程的环境表中,属于进程上下文的一部分。环境变量的核心特性:父进程的环境变量,fork 创建子进程时会被子进程继承。
环境变量的组织方式

每个程序都会收到⼀张环境表,环境表是⼀个字符指针数组,每个指针指向⼀个以'\0'结尾的环境
字符串
局部变量不会被子进程继承,环境变量可以被子进程继承,这是二者最核心区别。
一、变量分类:本地变量 vs 环境变量
1. 本地变量(自定义局部变量)
在当前 bash 终端内定义,仅在当前 shell 进程内部生效。fork 创建的子进程无法读取这个变量,终端关闭,变量直接销毁。
# 定义本地变量
local_val="我是本地变量"
# 查看变量
echo $local_val
新开子 shell 测试:
bash # 创建子shell
echo $local_val # 无输出,子进程拿不到本地变量
exit # 退出子shell
2. 环境变量(全局变量)
环境变量存放在进程的环境表中,当父进程 fork 生成子进程,子进程会拷贝父进程完整环境表,子进程可以读取父进程的环境变量。 使用export可以把本地变量提升为环境变量。
# 方式1:定义同时导出为环境变量
export env_val="我是环境变量"
# 方式2:先定义本地变量,再导出
test_val=100
export test_val
子 shell 测试:
bash
echo $env_val # 输出 我是环境变量,子进程继承成功
exit
二、查看环境变量的常用命令
1. env
直接打印所有环境变量,只会输出环境变量,不会输出 shell 本地变量。
env
2. echo $ 变量名,查看单个环境变量
$符号代表取变量的值
cpp
echo $PATH
echo $HOME
echo $USER
echo $SHELL
echo $PWD
3. set
输出本地变量 + 环境变量,输出范围比 env 更大。
4. export
不带参数执行 export,列出所有环境变量,效果近似 env。
5. unset 删除变量
无论是本地变量还是环境变量,都可以使用 unset 删除。
unset env_val
三、Linux 系统内置常用环境变量详解
表格
| 环境变量 | 作用说明 |
|---|---|
PATH |
最重要 ,可执行程序搜索路径。直接输入程序名,系统按 PATH 内目录顺序查找可执行文件,目录之间用冒号:分隔 |
HOME |
当前登录用户的家目录。不带参数执行cd,默认直接回到 $HOME |
USER |
当前登录的用户名 |
SHELL |
当前使用的命令解释器,一般是/bin/bash |
PWD |
当前所在工作目录,pwd命令本质就是读取这个环境变量 |
LOGNAME |
登录用户名,和 USER 含义相近 |
LD_LIBRARY_PATH |
动态库搜索路径,程序运行时优先从该目录寻找动态库 |
LANG |
系统语言字符集,控制终端中文 / 英文输出 |
重点深入理解 PATH
PATH 的值是多个目录,使用:隔开,示例:
PATH=/usr/local/bin:/usr/bin:/bin
执行ls时,操作系统的查找逻辑:
- 依次遍历 PATH 里面的目录,先去第一个目录
/usr/local/bin寻找名为 ls 的可执行文件; - 找到就直接执行,停止搜索;
- 找不到就继续去下一个目录查找;
- 全部目录查找完毕仍然找不到,提示
command not found。
小实验:自己写 C 程序编译生成 a.out,默认执行必须
./a.out。 如果把当前目录加入 PATH,就可以直接输入 a.out 运行:
export PATH=$PATH:.
$PATH代表保留原来 PATH 所有内容,.代表当前目录。 ⚠️ 安全提醒:不要长期把.放在 PATH 最前面,存在恶意程序安全隐患。
四、临时环境变量与永久环境变量
4.1 临时修改环境变量
使用 export 命令直接在终端修改,仅当前 bash 进程以及它 fork 出来的子进程生效,关闭终端之后修改全部失效。上面所有 export 示例都是临时修改。
4.2 永久修改环境变量
修改配置文件,每次登录 shell 自动加载配置,永久生效。分为用户级别和系统级别。
-
用户级别(只对当前登录用户生效,推荐使用) 配置文件:
~/.bashrc、~/.bash_profilevim ~/.bashrc
在文件末尾添加这一行
export MY_ENV="永久环境变量测试"
保存退出文件后,执行 source 加载配置,不需要重新登录终端:
source ~/.bashrc
source 原理:在当前 shell 进程 执行脚本,不会创建子 shell。 如果直接执行
./.bashrc,会新建子 shell 运行脚本,环境变量只在子 shell 生效,父终端不会生效。
- 系统级别(所有登录用户都会生效,需要 root 权限) 配置文件:
/etc/profile修改后,所有用户登录都会加载这个环境变量。修改系统级配置需要谨慎。
五、C 语言代码中获取环境变量
环境变量以字符串数组形式保存,数组每一项是key=value,数组最后一项是 NULL 标记结束。 C 语言有三种方式获取环境变量。
方式 1:main 函数第三个参数 env
main 完整原型:int main(int argc, char* argv[], char* env[]); env 就是环境变量字符串数组。
cpp
#include <stdio.h>
int main(int argc, char* argv[], char* env[])
{
//遍历打印全部环境变量
for(int i = 0; env[i]; i++)
{
printf("%s\n", env[i]);
}
return 0;
}
编译运行,输出结果等价于env命令。
方式 2:库函数 getenv(工程推荐写法)
头文件:#include <stdlib.h> 函数原型:char *getenv(const char *name); 传入环境变量名称字符串,返回对应 value 指针;环境变量不存在返回 NULL。 优点:不需要遍历全部环境变量,直接按名字查找,代码可读性高。
cpp
#include <stdio.h>
#include <stdlib.h>
int main()
{
char* path_val = getenv("PATH");
if(path_val != NULL)
{
printf("PATH = %s\n", path_val);
}
return 0;
}
方式 3:全局外部变量 environ
系统提供全局变量environ,类型char**,代表环境变量数组,不需要 main 传参也可以使用。
cpp
#include <stdio.h>
extern char** environ;
int main()
{
for(int i = 0; environ[i]; i++)
{
printf("%s\n", environ[i]);
}
return 0;
}
面试考点:三种获取方式对比,工程开发优先使用 getenv。
六、fork 父子进程,环境变量继承规则【核心重点】
- 父进程执行 export 设置环境变量之后,再 fork 创建子进程,子进程会拷贝一份父进程环境表,子进程可以继承环境变量。
- 子进程修改自己的环境变量,不会影响父进程。fork 采用写时拷贝,父子进程地址空间相互独立。
- 子进程创建完成之后,父进程后续新增 / 修改环境变量,已经存在的子进程不会感知到父进程的变化。
- 本地变量不会被子进程继承,只有 export 导出后的环境变量才会被子进程继承。
验证实验:
cpp
# 定义本地变量
var_local=100
# 导出环境变量
export var_env=200
# 创建子shell
bash
echo $var_local # 无输出,本地变量无法继承
echo $var_env # 输出200,环境变量继承成功
exit
七、source 命令详解(面试高频)
source 脚本名
# 简写 . (点)
. 脚本名
普通执行脚本./test.sh:会创建子 shell 进程执行脚本,脚本内部 export 的环境变量只在子 shell 生效,脚本执行结束子进程销毁,父 shell 完全不受影响。
source 执行脚本:不会创建新进程,直接在当前 shell 进程执行脚本代码。脚本里定义的环境变量,直接作用在当前终端。
所以修改
.bashrc之后,要用 source 生效,而不是直接运行文件。
八、面试高频问答
Q1:本地变量和环境变量的区别?
本地变量仅当前 shell 进程可见,fork 子进程无法继承;环境变量保存在进程环境表,fork 子进程会拷贝环境表,子进程可以继承环境变量。
Q2:环境变量什么时候被子进程继承?
必须是父进程已经 export 导出环境变量,然后再 fork 创建子进程,子进程才能拿到。子进程创建完成之后,父进程新增环境变量,子进程看不到。
Q3:command not found 报错有哪些原因?
- 命令 / 可执行文件本身不存在;
- 可执行文件存在,但是文件所在目录不在 PATH 环境变量的搜索目录中;
- 文件存在,但是当前用户没有可执行权限。 解决办法:使用绝对路径执行,或者把目录添加到 PATH。
Q4:source 和直接执行脚本 ./xxx.sh 的区别?
./xxx.sh:新建子 shell 运行脚本,脚本内环境变量只在子进程生效,父 shell 不受影响。 source xxx.sh:当前 shell 进程直接执行脚本,不创建子进程,脚本中修改的环境变量直接作用于当前终端。
Q5:子进程修改环境变量会影响父进程吗?
不会。fork 之后发生写时拷贝,父子进程环境表相互独立,子进程修改属于自己的副本,不会影响父进程。
九、小结
环境变量是以key=value存储在进程环境表中的字符串,是进程上下文的组成部分,核心特点是 fork 创建子进程时可以被子进程继承。 PATH 是日常开发最常用的环境变量,控制系统去哪里寻找可执行程序。 我们既可以在 shell 命令行操作环境变量,也可以在 C 代码中读取环境变量。区分临时生效、永久生效,理解父子进程环境变量继承规则,是本章核心。
