Linux 上的环境变量及生效范围

前言

在 Linux 中,环境变量可按「作用范围」从大到小分为四类:

层级 典型位置 简要说明
系统级 /etc/environment/etc/profile/etc/profile.d/ 所有用户登录时加载;
systemd/cron/init.d 默认不读
用户级 ~/.bashrc~/.bash_profile~/.profile~/.pam_environment 仅当前用户,登录或交互式 shell 时加载
会话级 当前 Shell 中 exportsource 仅当前终端会话及子进程
进程级 命令行 VAR=value commandenv VAR=value command 仅该命令及其子进程

要点速记:

  • 系统级/etc/environment 仅 PAM 登录会话生效,不支持 $VAR 引用;/etc/profile/etc/profile.d/*.sh登录 shell 时执行,非登录 shell 不加载。
  • 用户级~/.bashrc交互式非登录 shell 加载(新开终端);登录 shell 需在 ~/.bash_profile~/.profilesource ~/.bashrc 才会带上。
  • 服务/脚本:systemd 服务、cron、init.d 脚本不会自动读上述文件,需在服务配置或脚本里显式加载或使用 EnvironmentFile

配置位置对比

位置 适用场景 说明
~/.bashrc 当前用户、交互式 shell 新开终端时加载;登录 shell 需在 ~/.bash_profile 里 source
~/.profile 当前用户、登录 shell 图形/SSH 登录时生效;dash 等非 bash 也读
~/.bash_profile 当前用户、bash 登录 shell 查找顺序:~/.bash_profile~/.bash_login~/.profile,找到即停
/etc/environment 全局(所有用户) KEY=value,不支持 $VAR 与 shell 语法;PAM 登录才生效
/etc/profile/etc/profile.d/*.sh 全局、登录 shell 系统级配置,需 root;推荐用 profile.d 便于维护
命令行 export 当前会话 仅当前终端,关闭即失效,适合调试

/etc/environment 详解

/etc/environment 是系统级配置,特点如下:

  • 格式:仅支持 KEY=value,不能写 shell 语法或变量引用(如 $PATH)。
  • 加载时机:由 PAM 在用户登录时读取,不是 shell 脚本,不能 source
  • 生效范围:仅对 PAM 登录会话生效,init.d 脚本、systemd 服务、cron 不会读,因此不适合给服务用。

编辑与示例

1
sudo vi /etc/environment

内容示例(纯键值对):

1
JAVA_HOME="/usr/local/jdk/bisheng-jdk1.8.0_422"

生效方式:重新登录 SSH 或重启;若需当前终端立刻生效,只能在本终端手动 export,例如:

1
export JAVA_HOME="/usr/local/jdk/bisheng-jdk1.8.0_422"

服务中环境变量生效

/etc/profile 只在用户登录 shell 时被读取;init.d 脚本由 systemd/init 在系统级直接执行,不经过登录 shell,因此读不到 /etc/profile 里的变量。

做法:在 init.d 脚本开头用 . /etc/profilesource /etc/profile 显式加载。

init.d 脚本示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/bin/sh
# chkconfig: 2345 85 15
# description:auto_run

#Tomcat根位置
TOMCAT_ROOT=/data/tools/apache-tomcat-8.5.99/

# 手动加载 /etc/profile
. /etc/profile

javahome() {
echo "Show JAVA_HOME"
echo $JAVA_HOME
}

#开始方法
start() {
cd $TOMCAT_ROOT
./bin/startup.sh
}

#结束方法
stop() {
cd $TOMCAT_ROOT
./bin/shutdown.sh
}

case "$1" in
javahome)
javahome
;;
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
*)
echo "Usage: $0 {start|stop|restart|javahome}"
exit 1
esac

常见场景

选择哪个位置主要取决于以下三个因素:

  1. 作用范围:是全局生效(所有用户/服务)还是仅当前用户/应用生效?
  2. 服务启动方式:是通过 systemd 服务启动,还是手动命令行启动?
  3. 安全性:是否包含敏感信息(如密码、密钥)?

以下是针对不同场景的最佳实践建议:

部署应用程序

适用情况:Java、Python、Node.js、Go 等后端服务,尤其是由 systemd 管理的服务。
最佳位置应用专属的 .env 文件systemd 服务配置文件

理由:

  • 隔离性:变量只对该应用生效,不会污染系统环境。
  • 安全性:可以将文件权限设置为 600(仅所有者可读),避免其他用户窃取密钥。
  • 兼容性:解决了 systemd 不加载 ~/.bashrc 导致变量失效的经典问题。

操作方法:

方法 A (Systemd 推荐):

编辑服务单元 /etc/systemd/system/your-app.service

1
2
3
4
[Service]
User=appuser
EnvironmentFile=/opt/your-app/.env
ExecStart=/usr/bin/java -jar app.jar

保存后执行:

1
2
sudo systemctl daemon-reload
sudo systemctl restart your-app

方法 B (代码加载)

在代码中使用库(如 Python 的 python-dotenv 或 Node.js 的 dotenv)读取项目根目录下的 .env 文件。

全局系统配置(所有用户生效)

适用情况:安装 JDK, Maven, Go 等开发工具,需要所有用户都能使用命令。
最佳位置/etc/profile.d/*.sh (Linux 通用推荐)

理由:

  • 模块化:在 /etc/profile.d/ 下单独建脚本(如 java.sh),比直接改 /etc/profile 更清晰,系统升级时也不易被覆盖。
  • 语法支持:支持完整的 Shell 脚本语法(如 export VAR=$VAR:/new/path),而 /etc/environment 不支持变量引用。

操作方法:

1
sudo vim /etc/profile.d/my_custom_vars.sh

内容示例:

1
2
export JAVA_HOME=/usr/lib/jvm/java-11
export PATH=$PATH:$JAVA_HOME/bin

保存后,新登录的用户会自动生效,或执行:

1
source /etc/profile

注意:需要追加路径(如 PATH=$PATH:xxx)时不要用 /etc/environment,它不支持变量引用,只支持纯 KEY=value

仅当前用户生效(交互式终端)

适用情况:个人开发账号,设置别名、提示符或仅在终端操作时需要的变量。
最佳位置~/.bashrc (CentOS/Ubuntu 默认) 或 ~/.zshrc (如果使用 Zsh)。

理由:不影响其他用户,每次打开新终端窗口自动加载。

缺点如果是通过 systemd 启动的服务,在这里设置的变量通常无效,因为服务进程不经过交互式 Shell 登录。

操作方法:

1
2
echo 'export MY_VAR="hello"' >> ~/.bashrc
source ~/.bashrc

注意:修改后记得执行 source ~/.bashrc 或重新打开终端窗口。

包含高度敏感信息(数据库密码、API 密钥等)

最佳位置:云厂商密钥管理(如 KMS/ACM)或权限严格的 .env 文件

不要把明文密码写在 /etc/profile~/.bashrc 中:这些文件多为 644,同机其他用户可读,存在泄露风险。

推荐做法

  1. 将敏感项放在单独 .env,并执行 chmod 600 .env(仅所有者可读)。
  2. 在 systemd 的 [Service] 里用 EnvironmentFile=/path/to/.env 引入;不要用全局 profile。

总结对比表

配置文件/位置 作用范围 Systemd 服务 变量引用 $VAR 推荐用途
/etc/systemd/system/*.service(EnvironmentFile) 指定服务 ✅ 推荐 生产应用部署(首选)
/etc/profile.d/*.sh 全局 ❌ 不加载 全局工具(JDK、Maven 等)
~/.bashrc 当前用户、交互式 ❌ 不生效 个人别名、开发习惯
/etc/environment 全局 ❌ 不加载 简单静态全局变量
项目 .env 当前应用 ⚠️ 需代码或 EnvironmentFile 多环境配置(Dev/Prod)

常见问题

1. 在 ~/.bashrc 里加了变量,重启服务后没生效?

systemd 启动服务时不会启动登录/交互式 Shell,因此不会读 .bashrc。应改为在 unit 里用 EnvironmentFileEnvironment= 指定变量(推荐 EnvironmentFile)。

2. CentOS 和 Ubuntu 有区别吗?

逻辑一致。Ubuntu 可能更多用 /etc/environment 做极简配置,但若需要变量展开和脚本能力,两者都推荐用 /etc/profile.d/*.sh 做全局配置。

3. 修改后如何立即生效?

  • 用户级:source ~/.bashrc 或新开终端。
  • 全局级:source /etc/profile 或重新 SSH 登录。
  • 服务级:sudo systemctl daemon-reload && sudo systemctl restart <服务名>

结论

  • 跑业务服务:用 systemd 的 EnvironmentFile 指向权限严格的 .env
  • 装全局开发工具:用 /etc/profile.d/custom.sh
  • 仅自己终端用:用 ~/.bashrc(或 ~/.zshrc)。