在这里插入图片描述

从 PATH 到 export,从 getenv 到 environ,一篇讲透环境变量是什么、怎么用、从哪来


前言

你有没有想过这些问题?

  • 为什么在命令行输入 ls 就能运行,而你自己写的程序必须输入 ./myprog
  • 环境变量到底存在哪里?谁在管理它们?
  • 为什么子进程可以“继承”父进程的环境变量?
  • export 导出的变量,真的是“传给父进程”了吗?

如果你对这些问题模模糊糊,那这篇文章就是为你准备的。我们把环境变量的来龙去脉、底层原理、代码用法,一次性讲清楚。

🎯 一句话:环境变量是操作系统用来指定运行环境的参数,像一个个“隐形传令兵”,默默影响着每一个程序。


一、什么是环境变量?

概念 说明
环境变量 操作系统中用来指定操作系统运行环境的一些参数
作用 影响程序的行为,告诉程序“去哪里找东西”、“用什么配置”
特点 具有全局性,可以被子进程继承

📖 比喻:环境变量就像学校的校规。每个学生(进程)入学时都会拿到一份校规(环境变量),按照校规行事。校规变了,新入学的学生按新规,老学生不受影响(除非重新加载)。


二、最经典的环境变量:PATH

2.1 问题:为什么执行程序必须“找到它”?

在 Linux 中,要执行一个程序,必须先找到它。你输入 ls,bash 需要知道 ls 这个命令对应的二进制文件在哪里。

2.2 PATH 是什么?

PATH 是系统中搜索指令的默认搜索路径。当你输入一个命令时,bash 会按照 PATH 中列出的目录顺序,依次去这些目录里寻找同名可执行文件。

# 查看 PATH 的内容
echo $PATH
# 输出示例:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
路径 说明
/usr/local/bin 本地管理员安装的程序
/usr/bin 系统自带的用户程序
/bin 基础系统程序
/usr/sbin 系统管理员程序
/sbin 基础系统管理程序

🚗 比喻:PATH 就像手机里的应用搜索路径。你喊“打开计算器”,系统会去“所有应用列表”里找,而不需要你告诉它“计算器在 /system/app/Calculator.apk”。


2.3 为什么你写的程序必须加 ./?

因为你当前的目录(.)通常不在 PATH 中(出于安全考虑)。所以 bash 找不到你的程序,你必须明确告诉它:./myprog(当前目录下的 myprog)。

# 把当前目录加入 PATH(临时)
export PATH=$PATH:.
# 现在可以直接输入 myprog 运行了
myprog

三、环境变量的基本操作

3.1 命令速查表

命令 作用 示例
env 查看全部环境变量 env
echo $变量名 查看单个环境变量的值 echo $PATH
export 变量名=值 设置/导出环境变量 export MYNAME="Alice"
unset 变量名 取消环境变量 unset MYNAME
# 实战演示
export MY_VAR="hello"
echo $MY_VAR   # 输出 hello
unset MY_VAR
echo $MY_VAR   # 输出空行

四、环境变量从哪里来?

Q:环境变量的值被谁保存?谁在系统里查找命令?

A:bash(命令行解释器)内部维护了一个环境变量表(指针数组)。


Q:环境变量最开始从哪里来的?

A:从系统的相关配置文件中来的,例如:

配置文件 作用
/etc/profile 系统级全局配置,对所有用户生效
~/.bashrc 用户级配置,对当前用户的 bash 生效
~/.bash_profile 用户登录时加载
/etc/environment 系统环境变量(某些发行版)

当你打开一个终端时,bash 会依次加载这些配置文件,初始化环境变量表。

🏠 比喻:环境变量就像房子的默认设置。装修时(系统安装)会有全局默认值,你也可以自己修改(~/.bashrc),每次开门(开终端)都会按你的设置来。


五、获取环境变量的三种方法(代码层面)

方法一:通过 main 函数的第三个参数 env

#include <stdio.h>

int main(int argc, char *argv[], char *env[]) {
    // env 是一个字符串数组,以 NULL 结尾
    for (int i = 0; env[i] != NULL; i++) {
        printf("%s\n", env[i]);
    }
    return 0;
}

方法二:使用 getenv 函数(推荐)

#include <stdio.h>
#include <stdlib.h>

int main() {
    char *path = getenv("PATH");
    if (path != NULL) {
        printf("PATH = %s\n", path);
    }
    return 0;
}
参数 含义
argc 命令行参数个数
argv 命令行参数数组
env 环境变量数组(父进程传递过来的)

应用场景:写一个程序,只能我自己执行,其他人一律不能执行

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    char *user = getenv("USER");
    if (user == NULL || strcmp(user, "yourname") != 0) {
        printf("Permission denied: only yourname can run this program.\n");
        return 1;
    }
    printf("Welcome, master!\n");
    // 正常功能
    return 0;
}

🔐 这样编译出来的程序,只有用户 yourname 才能运行,其他人执行会报错。

方法三:使用全局变量 environ

#include <stdio.h>

extern char **environ;  // 声明外部全局变量

int main() {
    for (int i = 0; environ[i] != NULL; i++) {
        printf("%s\n", environ[i]);
    }
    return 0;
}
方法 优点 缺点
mainenv 参数 标准 需要修改函数签名
getenv 简单,按需获取 只能获取单个变量
extern char **environ 可以遍历全部 需要自己处理

六、环境变量的特性:全局性与继承

6.1 环境变量可以被子进程继承

当你在 bash 中 export 一个变量后,再运行任何程序(子进程),该程序都能通过 getenv 获取到这个变量。

export GREETING="Hello from bash"
./myprog   # myprog 中 getenv("GREETING") 会得到 "Hello from bash"

🌳 比喻:环境变量就像家族姓氏。父亲(父进程)姓“王”,儿子(子进程)也姓“王”。儿子可以有自己的名字,但姓氏从父亲那里继承。

6.2 环境变量通常具有全局性

这里的“全局”不是指整个系统,而是指在当前进程及其所有子进程中可见。

变量类型 是否被子进程继承 作用范围
环境变量 ✅ 是 当前进程 + 所有子进程
本地变量 ❌ 否 仅当前 shell 内部

七、补充概念:本地变量 vs 环境变量

7.1 本地变量

在命令行直接定义的变量(不加 export)是本地变量,只存在于当前 bash 内部,不会被子进程继承。

# 定义本地变量
MY_LOCAL="hello"
echo $MY_LOCAL   # 输出 hello

# 运行子进程
bash -c 'echo $MY_LOCAL'   # 输出空(子进程看不到)

7.2 查看所有变量(包括本地变量):set

set | grep MY_LOCAL   # 能看到 MY_LOCAL

set 命令显示当前 shell 中的所有变量(环境变量 + 本地变量 + 函数)。

7.3 将本地变量提升为环境变量:export

export MY_LOCAL     # 现在变成环境变量了
bash -c 'echo $MY_LOCAL'   # 输出 hello

八、深入理解 export:为什么不需要创建子进程?

问题:我们在 bash 里执行 export i,将本地变量 i 导入到环境变量中。但是之前不是说子进程和父进程是独立的吗?这里难道是在子进程里面创建的环境变量导入了父进程?

答案:不是!

export 是一个内建命令(built-in command)。内建命令的特点是:不需要创建子进程,而是由 bash 自己亲自执行(bash 自己调用函数或系统调用完成)。

命令类型 例子 是否创建子进程
外部命令 ls, gcc ✅ 是(fork + exec)
内建命令 export, cd, echo, set ❌ 否(bash 自己执行)

流程:

  1. 你在 bash 中输入 export i
  2. bash 识别出这是内建命令
  3. bash 直接调用内部函数,把自己的环境变量表里添加 i
  4. 没有创建子进程,所以不需要“传回”父进程——因为本来就在父进程里做的

🎯 核心:内建命令直接修改 bash 自身的内存数据,不经过 fork。


九、总结速查表

知识点 核心内容
环境变量 操作系统运行环境的参数
PATH 命令搜索路径,决定输入命令时去哪里找程序
env 查看全部环境变量
echo $VAR 查看单个环境变量
export 设置/导出环境变量(内建命令)
unset 取消环境变量
getenv() C 代码中获取环境变量(推荐)
extern char **environ 全局环境变量数组
环境变量继承 子进程自动获得父进程环境变量的副本
本地变量 不 export,仅当前 shell 可见
set 查看所有变量(包括本地变量)
内建命令 不创建子进程,bash 自己执行

最后

环境变量看似简单,但理解它的存储、继承、内建命令机制,能帮你:

· 理解 PATH 为什么重要
· 写出更安全的程序(比如用 getenv(“USER”) 做权限控制)
· 理解 export 到底在干什么
· 遇到环境变量相关 bug 时快速定位

动手试试:写一个 C 程序,用三种方法打印所有环境变量,看看你的 bash 传递了哪些“传令兵”。

Happy Coding! 🐧

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐