一、环境变量基础
1.1 什么是环境变量
环境变量是进程级别的键值对配置,每个进程都有一份独立的环境变量表。子进程创建时会从父进程继承一份副本,之后互不影响。
在 Linux 中,环境变量本质就是一组字符串,格式统一为 NAME=VALUE。
1.2 为什么需要环境变量
- 配置与代码分离:数据库地址、API 密钥不用写死在代码里
- 进程间信息传递:父进程给子进程传递运行上下文
- 系统全局约定:PATH、HOME、USER 等标准变量被所有程序遵循
- 多环境部署:同一套代码通过环境变量区分开发 / 测试 / 生产
二、Shell 中的环境变量操作
2.1 常用命令
bash
运行
# 查看所有环境变量
env
printenv
# 查看单个变量
echo $PATH
printenv PATH
# 设置临时变量(仅当前 Shell 有效)
export MY_VAR="hello"
# 只定义 Shell 变量,不进入环境
LOCAL_VAR="world"
# 删除变量
unset MY_VAR
# 临时给单个命令设置变量
MY_VAR=test ./myprogram
2.2 环境变量 vs Shell 变量
很多人混淆这两个概念:
- Shell 变量 :只在当前 Shell 内部有效,子进程继承不到(如
a=1) - 环境变量 :会被子进程继承(如
export a=1)
bash
运行
# 定义 Shell 变量
NAME="tom"
bash # 启动子 Shell
echo $NAME # 空的,继承不到
exit
# 导出为环境变量
export NAME="tom"
bash
echo $NAME # tom,可以继承
2.3 常见系统环境变量
表格
| 变量名 | 作用 | 示例 |
|---|---|---|
PATH |
可执行文件搜索路径,冒号分隔 | /usr/bin:/usr/local/bin:/sbin |
HOME |
当前用户家目录 | /home/zhangsan |
USER / LOGNAME |
当前用户名 | zhangsan |
SHELL |
默认登录 Shell | /bin/bash |
PWD |
当前工作目录 | /home/zhangsan/code |
OLDPWD |
上一次工作目录 | - |
LANG |
语言与字符集 | zh_CN.UTF-8 |
LD_LIBRARY_PATH |
动态库搜索路径 | /usr/local/lib |
PS1 |
命令提示符格式 | \u@\h:\w\$ |
2.4 PATH 的工作原理
当你输入 ls 而不是 /bin/ls 时,Shell 会按 PATH 中的目录顺序挨个查找:
- 先看
/usr/local/bin/ls存在吗? - 再看
/usr/bin/ls存在吗? - 找到第一个就执行,找不到就报
command not found
bash
运行
# 把当前目录加入 PATH(不推荐,有安全风险)
export PATH=.:$PATH
# 把 /opt/myapp/bin 追加到 PATH
export PATH=$PATH:/opt/myapp/bin
三、环境变量的持久化配置
环境变量按作用域分为三级,优先级从高到低:
3.1 三级作用域对比
表格
| 级别 | 配置文件 | 生效范围 | 失效时机 |
|---|---|---|---|
| 进程级 | export 命令 |
当前 Shell / 进程 | 关闭终端即失效 |
| 用户级 | ~/.bashrc ~/.bash_profile ~/.profile |
当前用户所有 Shell | 用户注销 |
| 系统级 | /etc/profile /etc/bash.bashrc /etc/environment |
所有用户 | 重启系统 |
3.2 Bash 启动加载顺序
登录式 Shell(如 ssh 登录、su -):
plaintext
/etc/profile → /etc/profile.d/*.sh → ~/.bash_profile → ~/.bashrc → /etc/bashrc
非登录式 Shell(如图形化终端、bash 命令):
plaintext
~/.bashrc → /etc/bashrc → /etc/profile.d/*.sh
最佳实践 :个人配置写在
~/.bashrc,系统全局配置写在/etc/profile.d/下新建 .sh 文件,不要直接修改/etc/profile。
3.3 让配置立即生效
bash
运行
source ~/.bashrc
# 或简写
. ~/.bashrc
四、C 语言操作环境变量
4.1 三种访问方式
方式 1:main 第三个参数 envp
c
运行
#include <stdio.h>
int main(int argc, char *argv[], char *envp[]) {
// envp 是环境变量数组,以 NULL 结尾
// 每个元素都是 "KEY=value" 格式的字符串
for (int i = 0; envp[i] != NULL; i++) {
printf("%s\n", envp[i]);
}
return 0;
}
方式 2:全局变量 environ
c
运行
#include <stdio.h>
extern char **environ; // libc 定义的全局变量
int main() {
char **p = environ;
while (*p) {
printf("%s\n", *p);
p++;
}
return 0;
}
方式 3:getenv 函数(最常用)
c
运行
#include <stdio.h>
#include <stdlib.h>
int main() {
// 读取 PATH
char *path = getenv("PATH");
if (path) {
printf("PATH = %s\n", path);
}
// 读取自定义变量,不存在则用默认值
char *mode = getenv("APP_MODE");
if (!mode) mode = "development";
printf("运行模式: %s\n", mode);
return 0;
}
4.2 修改环境变量
c
运行
#include <stdio.h>
#include <stdlib.h>
int main() {
// 设置环境变量
// 第三个参数 1 = 覆盖已存在的值,0 = 不覆盖
setenv("APP_ENV", "production", 1);
printf("APP_ENV = %s\n", getenv("APP_ENV"));
// 修改环境变量
setenv("APP_ENV", "debug", 1);
printf("修改后: %s\n", getenv("APP_ENV"));
// 删除环境变量
unsetenv("APP_ENV");
printf("删除后: %s\n", getenv("APP_ENV")); // (null)
// putenv 方式(不推荐,容易内存泄漏)
putenv("FOO=bar");
return 0;
}
重要 :进程内修改环境变量只影响当前进程及其子进程,绝对不会影响父进程(也就是你的 Shell)。这是最常见的误区。
4.3 继承与 fork/exec 的关系
plaintext
父进程(有环境变量 A=1)
│
├─ fork() → 创建子进程,复制环境变量表
│
└─ 子进程拥有独立的环境变量副本
│
└─ exec() → 加载新程序,默认保留原有环境变量
(execle 可以指定全新的环境变量)
示例:子进程继承环境变量
c
运行
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
setenv("TEST_VAR", "hello_from_parent", 1);
pid_t pid = fork();
if (pid == 0) {
// 子进程可以读到父进程设置的变量
printf("子进程: TEST_VAR = %s\n", getenv("TEST_VAR"));
// 子进程修改不影响父进程
setenv("TEST_VAR", "modified_by_child", 1);
printf("子进程修改后: %s\n", getenv("TEST_VAR"));
return 0;
}
wait(NULL);
printf("父进程: TEST_VAR = %s\n", getenv("TEST_VAR"));
// 输出还是 hello_from_parent
return 0;
}
五、/proc 文件系统查看环境变量
Linux 中每个进程的环境变量都可以通过 /proc 文件系统查看:
bash
运行
# 查看 PID=1234 的进程环境变量
cat /proc/1234/environ | tr '\0' '\n'
/proc/PID/environ 文件中,每个环境变量以 \0(空字符)分隔,所以用 tr '\0' '\n' 换成换行方便阅读。
六、最佳实践与常见坑
- 不要硬编码路径 :用
getenv("HOME")获取家目录,不要写死/home/xxx - 敏感信息放环境变量:数据库密码、API Key 不要提交到代码仓库
- 布尔变量判断存在性 :如
if (getenv("DEBUG"))来开启调试模式 - 提供默认值:环境变量不存在时要有合理的默认行为
- 不要在程序里改 PATH 然后指望 Shell 生效:子进程改不了父进程
- 环境变量名全大写:这是 Unix 世界的约定俗成
七、环境变量知识体系图
plaintext
环境变量
│
┌──────────────┼──────────────┐
│ │ │
基础概念 操作方式 生命周期
│ │ │
键值对 NAME=VALUE Shell命令 三级作用域
进程级独立副本 export/ unset 进程级>用户级>系统级
fork时继承 env/printenv /etc/profile
exec默认保留 $VAR 引用 ~/.bashrc
│ │ │
标准变量 C语言API 加载顺序
│ │ │
PATH搜索路径 getenv读取 登录式Shell
HOME家目录 setenv设置 非登录式Shell
USER用户名 putenv/ unset source立即生效
LANG语言 environ全局 子进程不影响父进程
LD_LIBRARY_PATH envp参数
谢谢