跳转至

变量、环境与 export

Shell 变量只存在于当前 Shell;执行 export 后,它才进入当前 Shell 的环境,并在创建子进程时传给子进程。

当前 Shell
  ├─ local shell variable
  └─ exported environment variable
          ↓ fork/exec 时复制
       子进程环境

子进程不能反向修改父 Shell 环境。脚本直接执行与 source 的效果因此不同。

定义与引用

app_env=production       # 等号两侧不能有空格
printf '%s\n' "$app_env"
readonly app_home=/opt/app
unset app_env

变量名区分大小写。Shell 自用变量通常使用小写,环境变量惯例使用大写,避免覆盖 PATHHOMEIFS 等系统变量。

export

APP_ENV=production
export APP_ENV

# 等价简写
export LOG_LEVEL=INFO

验证继承:

local_only=one
export inherited=two
bash -c 'printf "local=%s inherited=%s\n" "$local_only" "$inherited"'

只给一条命令设置环境:

APP_ENV=test LOG_LEVEL=DEBUG ./run.sh

这不会永久修改当前 Shell 的变量,适合测试一次命令。

source 与执行脚本

./env.sh          # 在子进程执行,export 不会回到当前 Shell
source ./env.sh   # 在当前 Shell 执行,会修改当前环境
. ./env.sh        # POSIX 写法

不要 source 不可信脚本,因为其中任何命令都会在当前 Shell 权限下执行。

PATH

export PATH="/opt/app/bin:$PATH"
command -v appctl
type -a appctl

把新目录放前面会覆盖同名系统命令;放后面则只作为补充。不要写:

export PATH=/opt/app/bin

这会丢失 /usr/bin 等原路径。也不要把当前目录 . 放在 PATH 前面,防止执行恶意同名文件。

常见启动文件

场景 Bash 常见文件
登录 Shell /etc/profile~/.bash_profile
交互非登录 Shell ~/.bashrc
systemd 服务 Unit 的 Environment=/EnvironmentFile=,通常不读取用户 bashrc
cron 环境非常精简,应显式 PATH 和变量
Docker/Compose environmentenv_file,与宿主 Shell 插值要区分

SSH、sudo、systemd、cron、容器各有不同环境继承规则。终端中 export 成功,不代表 systemd 服务能看到。

查看环境

env | sort
printenv PATH
export -p
declare -p APP_ENV

# Linux 查看进程环境,内容可能包含 Secret
tr '\0' '\n' < /proc/<pid>/environ

读取进程环境前注意权限和敏感信息,不要把完整输出发到工单。

参数展开

value=${APP_ENV:-development}       # 未设置或为空时用默认值
value=${APP_ENV:=development}       # 使用默认值并赋值
: "${DB_HOST:?DB_HOST is required}" # 未设置或为空则报错退出
length=${#APP_ENV}
file=${path##*/}                    # 去掉最长目录前缀

${var:-default} 不会把默认值写回变量;${var:=default} 会。脚本处理必填参数时使用 :? 比运行到中途才报空地址更安全。

Secret 边界

环境变量可能出现在 /proc、进程转储、调试信息、容器 inspect 或 CI 日志。密码和 Token 不要写进 Shell 历史、命令行参数或仓库;优先使用权限受控文件、Secret 管理系统和短期凭据。