【Linux系统】09 环境变量

前言

前面我们学习了进程、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时,操作系统的查找逻辑:

  1. 依次遍历 PATH 里面的目录,先去第一个目录/usr/local/bin寻找名为 ls 的可执行文件;
  2. 找到就直接执行,停止搜索;
  3. 找不到就继续去下一个目录查找;
  4. 全部目录查找完毕仍然找不到,提示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 自动加载配置,永久生效。分为用户级别和系统级别。

  1. 用户级别(只对当前登录用户生效,推荐使用) 配置文件:~/.bashrc 、~/.bash_profile

    vim ~/.bashrc

    在文件末尾添加这一行

    export MY_ENV="永久环境变量测试"

保存退出文件后,执行 source 加载配置,不需要重新登录终端:

复制代码
source ~/.bashrc

source 原理:在当前 shell 进程 执行脚本,不会创建子 shell。 如果直接执行./.bashrc,会新建子 shell 运行脚本,环境变量只在子 shell 生效,父终端不会生效。

  1. 系统级别(所有登录用户都会生效,需要 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 父子进程,环境变量继承规则【核心重点】

  1. 父进程执行 export 设置环境变量之后,再 fork 创建子进程,子进程会拷贝一份父进程环境表,子进程可以继承环境变量。
  2. 子进程修改自己的环境变量,不会影响父进程。fork 采用写时拷贝,父子进程地址空间相互独立。
  3. 子进程创建完成之后,父进程后续新增 / 修改环境变量,已经存在的子进程不会感知到父进程的变化。
  4. 本地变量不会被子进程继承,只有 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 报错有哪些原因?

  1. 命令 / 可执行文件本身不存在;
  2. 可执行文件存在,但是文件所在目录不在 PATH 环境变量的搜索目录中;
  3. 文件存在,但是当前用户没有可执行权限。 解决办法:使用绝对路径执行,或者把目录添加到 PATH。

Q4:source 和直接执行脚本 ./xxx.sh 的区别?

./xxx.sh:新建子 shell 运行脚本,脚本内环境变量只在子进程生效,父 shell 不受影响。 source xxx.sh:当前 shell 进程直接执行脚本,不创建子进程,脚本中修改的环境变量直接作用于当前终端。

Q5:子进程修改环境变量会影响父进程吗?

不会。fork 之后发生写时拷贝,父子进程环境表相互独立,子进程修改属于自己的副本,不会影响父进程。

九、小结

环境变量是以key=value存储在进程环境表中的字符串,是进程上下文的组成部分,核心特点是 fork 创建子进程时可以被子进程继承。 PATH 是日常开发最常用的环境变量,控制系统去哪里寻找可执行程序。 我们既可以在 shell 命令行操作环境变量,也可以在 C 代码中读取环境变量。区分临时生效、永久生效,理解父子进程环境变量继承规则,是本章核心。

相关推荐
Teecertlabs1 小时前
【机密计算顶会解读】15:LDR——面向嵌入式TEE系统的高效安全Linux驱动
linux·操作系统·机密计算·可信执行环境
程序员-Benothing1 小时前
Linux网络性能分析:ss、netstat、iftop、sar实战
linux·运维·服务器
其实防守也摸鱼1 小时前
Codex 下载与本地部署实战指南
linux·服务器·前端·数据库·算法·ai
岳麓丹枫0011 小时前
故障案例分析:系统负载居高不下,多次出现 OOM
linux·postgresql
2401_862880821 小时前
Linux中断、中断子系统、内核同步互斥机制
linux·驱动开发·嵌入式硬件
姓刘的哦2 小时前
GT911触摸芯片
linux·stm32
Android系统攻城狮2 小时前
Linux Gstreamer深度解析之gst_audio_ring_buffer_read调用流程与实战(七十五)
linux·运维·服务器·gstreamer音视频·gstreamer音视频进阶
一条破秋裤2 小时前
04_控制GPIO通用化_从寄存器到gpiod
linux
星恒随风2 小时前
Linux进程信号(二):深入理解信号阻塞、未决、sigaction、中断机制、可重入函数与SIGCHLD
linux·运维·服务器·笔记·学习