变量在什么时候被加载
Linux 的环境变量不是全局一处配置就到处生效:登录 shell 与交互 shell 加载的文件不同,非交互脚本、cron 与 systemd 服务基本不读用户 profile。把 JAVA_HOME 之类变量写进 ~/.bashrc,就会出现『终端里好用、面板里或服务里找不到命令』的现象。关键是把变量放对层级:系统级放 /etc/profile.d,用户级区分登录与交互,服务级放 unit 文件。
基本信息
| 系统级登录 | /etc/profile、/etc/profile.d/*.sh |
|---|---|
| 用户级登录 | ~/.bash_profile、~/.bash_login、~/.profile |
| 用户级交互 | ~/.bashrc |
| 服务级 | systemd unit 的 Environment= 与 EnvironmentFile= |
| 临时生效 | export 仅影响当前会话 |
| 权限要求 | 写系统级目录需 root;profile.d 脚本应有合理权限避免被非授权改写 |
典型使用场景
- 统一设置 JAVA_HOME、PATH 等全局变量,供所有用户与脚本使用
- 给单个业务用户配置专属变量而不污染系统
- 让开机自启的服务读到必要变量,避免手动重启才正常
- 批量终端迁移时统一 PATH,兼容老脚本里的命令路径
- 排查『同一条命令在不同入口下行为不一致』的问题
全局配置示例
全局配置示例
sudo tee /etc/profile.d/app-env.sh >/dev/null <<'EOF'
# 全局变量:登录 shell 会自动读取该目录下的 .sh 文件
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
export PATH=$JAVA_HOME/bin:$PATH
EOF
chmod 644 /etc/profile.d/app-env.sh # 权限收紧,避免被非授权写入
source /etc/profile.d/app-env.sh # 当前会话立即生效
bash -lc 'echo $JAVA_HOME' # 新登录 shell 验证,确认持久生效服务与脚本场景
服务与脚本场景
# systemd 服务不读 profile,变量写在 unit 里:
# [Service]
# Environment=JAVA_HOME=/usr/lib/jvm/java-17-openjdk
# EnvironmentFile=-/etc/sysconfig/myapp
systemctl show myapp -p Environment # 查看服务实际拿到的变量
env -i bash -lc 'echo $PATH' # 模拟最小环境,检查登录加载链是否完整
crontab -l | head # cron 环境极简,脚本里最好显式 source 或写全路径放错位置的典型症状
| 变量写在哪 | 谁会读到 | 典型症状 |
|---|---|---|
| ~/.bashrc | 交互 shell | 图形界面启动的终端有效,SSH 非交互命令无效 |
| /etc/profile.d | 所有登录 shell | 交互登录可见;systemd 服务仍读不到 |
| ~/.bash_profile | 该用户登录 shell | 其他用户与 root 下命令找不到 |
| systemd Environment= | 仅该服务 | 手工执行脚本正常,服务启动仍报找不到命令 |
| export 当前会话 | 仅当前终端 | 关掉窗口后全部失效 |
非交互 shell、cron 与 systemd 服务都不会加载 profile 类文件:服务依赖的变量应写进 unit 的 Environment 或 EnvironmentFile,脚本里则用绝对路径或显式 source。
/etc/profile.d 下的脚本会对每次登录执行,权限过宽等于给所有用户留了开机执行代码的入口,应保持 644 且属主为 root。