这是《Linux 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 环境变量影响命令查找、语言 locale、代理、密钥注入、运行时参数和容器配置。它也是生产问题的高发点:本机能跑、systemd 起不来;终端里有代理、服务里没有;CI 有变量、线上没有。本章讲清环境变量的作用域、加载顺序和治理方式。
13.1 基本操作
查看:
env
printenv
echo "$PATH"
echo "$HOME"
echo "$LANG"
设置:
export APP_ENV=production
export JAVA_OPTS="-Xms1g -Xmx1g"
仅当前命令生效:
APP_ENV=production ./server
LANG=C sort file.txt
删除:
unset APP_ENV
只对当前 Shell 生效。终端关闭后消失。
13.2 Shell 变量与环境变量
app=order
export app
env | grep '^app='
区别:
| 类型 | 当前 Shell | 子进程 |
|---|---|---|
| Shell 变量 | 可见 | 不可见 |
| 环境变量 | 可见 | 可见 |
查看进程环境:
tr '\0' '\n' < /proc/<pid>/environ
进程启动后修改 Shell 环境不会影响已启动进程。
13.3 PATH
查看:
echo "$PATH"
type java
which java
command -v java
临时添加:
export PATH="$PATH:/opt/app/bin"
优先级按目录顺序决定:
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
安全原则:
- 不把
.放入 PATH; - 不把可写目录放在高优先级;
- 使用绝对路径执行关键命令;
- 检查命令别名;
- 不信任来路不明的 PATH。
检查别名:
alias
type -a ls
13.4 登录 Shell 加载顺序
Bash 常见顺序:
/etc/profile
-> ~/.bash_profile 或 ~/.bash_login 或 ~/.profile
-> interactive non-login 时读取 ~/.bashrc
常见文件:
| 文件 | 场景 |
|---|---|
/etc/profile |
系统全局登录 |
/etc/profile.d/*.sh |
软件包或团队全局配置 |
~/.bash_profile |
用户登录 |
~/.profile |
兼容登录 |
~/.bashrc |
交互非登录 |
验证当前 Shell:
echo "$0"
shopt login_shell
SSH 登录、su、cron、systemd、容器入口的加载路径不同。服务变量应在服务配置中显式声明。
13.5 systemd 环境变量
方式一:
[Service]
Environment="APP_ENV=production"
Environment="JAVA_OPTS=-Xms1g -Xmx1g"
方式二:
[Service]
EnvironmentFile=/etc/order/env
文件格式:
APP_ENV=production
JAVA_OPTS=-Xms1g -Xmx1g
DB_PASSWORD_FILE=/run/secrets/db_password
查看:
systemctl show app -p Environment
sudo systemd-run --wait --property=EnvironmentFile=/etc/order/env env
修改后:
sudo systemctl daemon-reload
sudo systemctl restart app
13.6 cron 环境
cron 环境通常比登录 Shell 精简,PATH 和 Shell 可能不同。
导出任务环境:
crontab -l
示例:
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=""
30 2 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1
脚本内部不要假设交互 Shell 环境,必要时显式 source 可信配置或直接写绝对路径。
13.7 容器环境变量
Docker:
docker run \
--env APP_ENV=production \
--env-file ./prod.env \
my-app:1.0.0
查看:
docker inspect <container> \
--format '{{range .Config.Env}}{{println .}}{{end}}'
docker exec <container> env
Kubernetes:
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: order
image: registry.example.internal/order:1.0.0
env:
- name: APP_ENV
value: production
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: order-db
key: password
不要把数据库密码写进镜像层、Deployment YAML 或命令历史。
13.8 敏感信息治理
常见错误:
| 做法 | 风险 |
|---|---|
| 写入 Dockerfile ENV | 进入镜像历史 |
| 写入 Git 明文配置 | 泄露和审计困难 |
| 写入命令行参数 | 进入进程列表和 Shell 历史 |
| 全局 export | 影响范围过大 |
| 团队共享账号 | 无法追责 |
推荐:
- 使用 Secret Manager 或 Kubernetes Secret 加访问控制;
- 只注入需要的进程;
- 定期轮换;
- 审计访问;
- 日志脱敏;
- 本地开发使用独立示例配置。
13.9 locale 与时区
查看:
locale
timedatectl
date
cat /etc/timezone
设置:
sudo localectl set-locale LANG=en_US.UTF-8
sudo timedatectl set-timezone Asia/Shanghai
容器:
docker run --env TZ=Asia/Shanghai my-app:1.0.0
多语言和排序结果受 locale 影响。数据库、日志采集器和应用应统一字符集与时区。
13.10 代理变量
常见变量:
HTTP_PROXY=http://proxy.internal:3128
HTTPS_PROXY=http://proxy.internal:3128
NO_PROXY=.example.internal,127.0.0.1,10.0.0.0/8
使用:
export HTTPS_PROXY=http://proxy.internal:3128
curl -v https://example.com
取消:
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY
代理配置只对尊重这些变量的工具生效。系统 DNS、某些运行时和数据库驱动可能需要单独配置。
13.11 排障案例
systemd 服务找不到命令
systemctl cat app
systemctl show app -p Environment
journalctl -u app -n 100
command -v java
修复:
[Service]
Environment="PATH=/opt/java/bin:/usr/local/bin:/usr/bin:/bin"
ExecStart=/opt/java/bin/java -jar /opt/app/app.jar
终端正常,cron 失败
grep -R 'PATH' /etc/crontab /var/spool/cron 2>/dev/null
bash -x /opt/scripts/job.sh
常见差异:
- PATH;
- HOME;
- LANG;
- TERM;
- 交互初始化文件;
- sudo 环境。
容器变量没有生效
docker inspect <container> --format '{{json .Config.Env}}' | jq .
kubectl get pod <pod> -o jsonpath='{.spec.containers[0].env}'
kubectl describe pod <pod>
检查:
- 变量名大小写;
- Pod 是否重建;
- ConfigMap 或 Secret 是否更新;
- 应用是否启动时读取;
- 挂载路径和 key 是否正确。
本章小结
环境变量有明确作用域:命令、Shell、用户、systemd、cron、容器和编排系统不同。生产服务应在 systemd unit、容器声明或 Secret Manager 中显式配置,不依赖登录 Shell。敏感变量要控制注入范围、轮换和审计。
思考题
- Shell 变量和环境变量的区别是什么?
- 为什么 systemd 服务不能依赖
~/.bashrc? - cron 任务为什么常常因 PATH 失败?
- 容器 ENV 为什么不适合保存数据库密码?
- 设计一个服务的配置与环境变量分层方案。