export 命令详解与参数实测(Ubuntu 22.04)

基于 Ubuntu 22.04(bash 5.1.16)逐项实测 export:declare -x 导出清单、定义并导出、只赋值不导出的子进程可见性差异、-n 取消导出、-f 导出函数、readonly 后导出报错、一次导出多变量、export -p/env/printenv 三方对照、PATH/LANG/HOME 实战、env 临时注入与干净环境、管道与子 shell 赋值不回传陷阱及修正。

Linux命令Linux系统UbuntuUbuntu 22.04命令行Shell2026/10/16

export 命令详解与参数实测(Ubuntu 22.04)

目标:全面掌握 export 的每一种用法——-p 列出全部导出变量、定义并导出、只赋值不导出的子进程可见性差异、-n 取消导出、-f 导出函数、readonly 变量导出报错、一次导出多个变量、export -p/env/printenv 三方对照、PATH/LANG/HOME 常用导出、env 临时注入与 env -i 干净环境、管道与子 shell 赋值不回传陷阱及三种修正,全部命令与回显已在 Ubuntu 22.04(bash 5.1)实测通过。

一、环境信息

本文所有命令与回显均在以下环境实测采集,读者在相同版本的 Ubuntu 上可直接复现:

项目实测值
操作系统Ubuntu 22.04.5 LTS
架构x86_64
ShellGNU bash,版本 5.1.16(1)-release
export 命令类型shell builtin(Shell 内置命令,非外部可执行文件)
实测演示目录/root/demo/export
环境变量持久化文件/root/.bashrc

环境确认命令与实测回显如下:

$ head -2 /etc/os-release
PRETTY_NAME="Ubuntu 22.04.5 LTS"
NAME="Ubuntu"

$ uname -m
x86_64

$ echo BASH_VERSION=$BASH_VERSION
BASH_VERSION=5.1.16(1)-release

$ type export
export is a shell builtin

💡 说明:export 是 bash 内置命令,没有独立的可执行文件,也没有 --version 类选项;版本信息即 bash 版本(见 2.3 节)。

⚠️ 关于回显中的报错前缀:本文部分回显采集自实测脚本(如 /root/export-test.sh),错误行前缀 /root/export-test.sh: line 3: 是 shell 报错定位(脚本路径与行号);读者在交互式终端手工执行同样命令时,对应前缀为 bash:。命令本身在两种场景通用。

二、命令概览

2.1 语法与测试数据

export 给 shell 变量打上"导出属性",使其随进程环境传递给之后启动的子进程。语法:

export [-fn] [name[=value] ...]   # 定义并导出;-n 取消导出;-f 针对函数
export -p                          # 打印全部导出变量与函数

要点解读:

  • export VAR=value:赋值与导出一步完成,等价于先 VAR=value 再 export VAR;
  • 导出是单向向下的:只有当前 shell 之后启动的子进程能看到,子进程中的修改不会传回父进程(见 4.5 节与五、Q5);
  • export 不落盘——持久化必须写入 ~/.bashrc 等启动文件(见 4.4 节)。

后续示例使用以下演示数据(一个可执行的 hello 脚本与一个多行文本文件):

$ mkdir -p /root/demo/export/bin

$ printf '#!/bin/bash\necho hello-from-demo-bin\n' > /root/demo/export/bin/hello

$ chmod +x /root/demo/export/bin/hello

$ ls -l /root/demo/export/bin
total 4
-rwxr-xr-x 1 root root 37 Oct 15 21:18 hello

2.2 help 全量输出(逐行照抄)

export 是内置命令,官方帮助通过 help export 获取。以下为完整实测输出(逐行照抄,未做删改):

$ help export
export: export [-fn] [name[=value] ...] or export -p
    Set export attribute for shell variables.
    
    Marks each NAME for automatic export to the environment of subsequently
    executed commands.  If VALUE is supplied, assign VALUE before exporting.
    
    Options:
      -f	refer to shell functions
      -n	remove the export property from each NAME
      -p	display a list of all exported variables and functions
    
    An argument of `--' disables further option processing.
    
    Exit Status:
    Returns success unless an invalid option is given or NAME is invalid.

逐句解读这份帮助:

  • Marks each NAME for automatic export to the environment of subsequently executed commands:被打标记的变量会自动进入"之后执行的命令"的环境——方向是** subsequent(之后启动的)**子进程,这正是 3.3 节核心实验的理论依据;
  • If VALUE is supplied, assign VALUE before exporting:给值则先赋值再导出,一步完成;
  • -f:把操作对象从变量换成函数(3.8 节实测);
  • -n:移除导出属性,变量本身保留在当前 shell(3.6 节实测);
  • -p:打印全部导出变量与函数(3.1 节实测);
  • Exit Status:选项非法或 NAME 非法才失败;导出只读变量的失败是"赋值动作"被拒(3.9 节实测 rc=1)。

2.3 版本信息

export 无独立版本号,版本跟随 bash。实测:

$ bash --version | head -1
GNU bash, version 5.1.16(1)-release (x86_64-pc-linux-gnu)

本文全部行为均在 bash 5.1.16 上验证,其他 5.x 版本行为一致。

三、参数逐项实测

3.1 无参数 / -p:列出全部导出变量

不带参数的 export 与 export -p 等价,按 declare -x VAR="value" 格式逐行列出当前 shell 中所有带导出属性的变量。清单较长,用 head 与 wc -l 取节选与总数:

$ export -p | head -8
declare -x HOME="/root"
declare -x HOSTNAME="f1034b9326b2"
declare -x OLDPWD
declare -x PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
declare -x PWD="/"
declare -x SHLVL="1"

$ export -p | wc -l
6

输出解读:

  • 每行都是 declare -x 前缀 + 变量名 + ="值"——整段输出可直接粘贴回 shell 重新执行,完整复刻导出状态;
  • declare -x OLDPWD 一行没有等号和值:这是"已打导出标记但当前值为空"的形态(对照 3.4 节的空导出变量);
  • 主机名(HOSTNAME= 后的值)是每台机器系统分配的,读者机器上会显示自己的主机名。

💡 要点:export -p 是"导出状态快照"——排查"子进程为什么读不到变量"时,第一步就是 export -p | grep 变量名,命中即说明导出属性在,问题出在子进程侧(如被启动脚本覆盖);无命中则说明没导出或已被 -n 取消。

3.2 定义并导出:export DEMO_VAR=hello

最常用形态,赋值与导出一步完成:

$ export DEMO_VAR=hello

$ echo $DEMO_VAR
hello

echo $DEMO_VAR 在当前 shell 打出值,只证明"变量存在";它是否已进入环境,要靠子进程验证——这正是下一节的核心实验。

💡 要点:export VAR=value 与 VAR=value; export VAR 完全等价(help 原文 "assign VALUE before exporting");但 export VAR=value 少一次引用变量的机会,是最不易出错的习惯写法。

3.3 只赋值不导出:子进程可见性核心实验

这是 export 存在意义的直接验证:同一变量,导出前后子进程的可见性完全不同。

$ VAR2=only-local

$ bash -c 'echo child-saw: $VAR2'; echo rc=$?
child-saw:
rc=0

$ export VAR2=now-exported

$ bash -c 'echo child-saw: $VAR2'
child-saw: now-exported

$ printenv VAR2
now-exported

结果解读:

  • 只赋值阶段:VAR2=only-local 后,子进程 bash -c 'echo ... $VAR2' 输出 child-saw:(变量为空)——shell 变量默认不出环境,rc=0 只是 echo 的退出码,与变量无关;
  • 导出之后:export VAR2=now-exported 再开子进程,child-saw: now-exported——导出属性让变量进入环境块,随进程创建传递给子进程;
  • printenv VAR2 直接从进程环境取值,输出 now-exported,三方互证。

💡 技术拓展:bash 变量按"可见范围"分三个圈层,export 是从内圈向外圈的"升级"动作——

圈层定义方式当前 shell子进程典型例子
函数局部变量local VAR=x(仅函数体内)函数内可见❌函数内计数器、临时值
shell 变量VAR=x✅ 可见❌循环变量、会话临时值
环境变量export VAR=x✅ 可见✅ 可见PATH、HOME、LANG

本文 3.3 节实测的正是 shell 变量 → 环境变量的升级;反向用 -n 降级见 3.6 节。配置 PATH/LANG 这类"子进程也要用"的值必须走第三圈层。

3.4 export 未赋值名字:创建空导出变量

export 只给名字不给值,会创建一个值为空的导出变量(而不是报错):

$ export NOSUCH_X

$ export -p | grep NOSUCH_X
declare -x NOSUCH_X

$ bash -c 'echo NOSUCH_X-saw: [$NOSUCH_X]'
NOSUCH_X-saw: []

解读:

  • export -p 里出现无值的 declare -x NOSUCH_X,与 3.1 节的 OLDPWD 同形态;
  • 子进程能看到这个变量,只是值为空([])——"名字可见、值为空"。

💡 要点:这个形态常见于"先占位、后赋值"的写法(如启动脚本开头 export APP_MODE,具体值由外部覆盖);但它也是排障陷阱——变量名拼错时 export 照样"成功",直到子进程读到空值才暴露,务必用 export -p | grep 核对。

3.5 一次导出多个变量

export 支持一条命令带多个 name[=value]:

$ export E1=one E2=two E3=three

$ export -p | grep -E 'declare -x E[123]'
declare -x E1="one"
declare -x E2="two"
declare -x E3="three"

💡 要点:空格分隔的多个赋值会依次完成;混合形态也合法(如 export A=1 B——A 赋值导出,B 只导出已有值)。批量导出后建议跟一条 export -p | grep 核对,防止其中某个名字拼写失误(对照 3.4 节)。

3.6 -n:取消导出(变量本身保留)

-n 移除导出属性。变量在当前 shell 依然存在,但不再传给新的子进程:

$ export -n VAR2

$ echo shell-still-has: $VAR2
shell-still-has: now-exported

$ bash -c 'echo child-saw: $VAR2'; echo rc=$?
child-saw:
rc=0

$ export -p | grep VAR2; echo rc=$?
rc=1

解读:

  • 当前 shell 中 echo $VAR2 仍输出 now-exported——-n 只摘掉导出标记,不清空变量;
  • 新子进程读不到了(child-saw: 为空);
  • export -p | grep VAR2 无命中且 rc=1——导出清单里已无此变量,验证彻底。

💡 要点:export -n 的典型用途是"临时借道"——先导出让某个子命令用一次,随后 -n 收回,避免变量扩散到后续所有子进程;它与 unset VAR 的区别是后者连变量一起删除。

3.7 三方对照:export -p / env / printenv

三个命令都能看"环境",但视角不同。先用 env 与 printenv 各取节选:

$ env | head -8
HOSTNAME=f1034b9326b2
PWD=/
HOME=/root
SHLVL=0
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

$ env | wc -l
6

$ printenv | head -4
HOSTNAME=f1034b9326b2
PWD=/
HOME=/root
SHLVL=0

$ printenv HOME
/root

再用刚导出的 DEMO_VAR 做三方核对:

$ export -p | grep DEMO_VAR
declare -x DEMO_VAR="hello"

$ env | grep DEMO_VAR
DEMO_VAR=hello

$ printenv DEMO_VAR
hello

三方输出格式与用途对比:

命令类型输出格式能否查单个特色用途
export -pbash 内置declare -x NAME="value"配合 grep可回放的导出状态快照,含值为空的导出名
env外部命令 /usr/bin/envNAME=value否(配合 grep)可临时注入变量启动命令(4.1 节)、env -i 清空环境(4.2 节)
printenv外部命令 /usr/bin/printenvNAME=value✅ printenv HOME脚本中直接取单个环境变量值,rc 可判断变量是否存在

两份清单的差集也有信息量:export -p 里的 OLDPWD(值为空)没有出现在 env 中,而 env 里的 _=/usr/bin/env(刚执行的外部命令路径)不在 export -p 中——因为 export -p 列的是当前 shell 视角的导出属性,env/printenv 显示的是实际传给子进程的环境块,bash 对部分内部变量做了特殊处理,两侧并非逐行相等。

💡 要点:日常排查顺序推荐——printenv 变量名(最快确认子进程视角)→ 无结果时 export -p | grep 变量名(确认父 shell 侧是否导出)→ 都无结果说明变量根本没赋值或在别的会话里赋的。

3.8 -f:导出函数

-f 把导出对象从变量换成函数。函数经环境以 BASH_FUNC_函数名%% 为名传递,子 bash 启动时还原:

$ demo_func() { echo 'child func runs, arg:' $1; }

$ export -f demo_func

$ bash -c 'demo_func from-parent'
child func runs, arg: from-parent

$ printenv BASH_FUNC_demo_func%%
() {  echo 'child func runs, arg:' $1
}

解读:

  • 未导出时,bash -c 的子进程里没有 demo_func,会报 command not found;export -f 之后子 bash 正常调用并收到参数 from-parent;
  • printenv BASH_FUNC_demo_func%% 直接看到函数在环境里的编码形态——名字是 BASH_FUNC_demo_func%%,值是函数体原文(bash 5.x 用这种带 %% 的命名格式编码导出函数,防止环境串被当作函数体注入执行);
  • 该机制只在子 bash 间生效;非 bash 子进程(如 C 程序)拿到的只是一个环境串,不会还原成函数。

💡 要点:export -f 适合把校验、日志等小工具函数"递"给子脚本;跨 bash 版本/非 bash 解释器场景不要依赖它,通用做法仍是把函数固化成脚本文件放入 PATH。

3.9 只读变量导出报错:readonly 之后 export 被拒

变量一旦 readonly,任何赋值动作都被拒绝——export RO_VAR=changed 中包含赋值,同样失败:

$ readonly RO_VAR=frozen

$ export RO_VAR=changed; echo rc=$?
/root/export-test.sh: line 3: RO_VAR: readonly variable
rc=1

$ echo RO_VAR-still: $RO_VAR
RO_VAR-still: frozen

解读:

  • 报错 RO_VAR: readonly variable,rc=1(help 的 Exit Status:"NAME is invalid" 的实测形态之一);
  • 原值 frozen 未被改动——赋值在展开阶段就被 bash 拒绝;
  • 注意区别:对已 readonly 的变量执行不带赋值的 export RO_VAR 是可以的(只是打导出标记,不赋值)。

💡 要点:想"导出且防改",正确姿势是先 export VAR=值 再 readonly VAR——先锁定导出状态,再冻结内容;顺序反了(先 readonly)就只能只读地导出当前值,无法再赋初值。

3.10 常用环境变量导出:PATH / LANG / HOME

先看 PATH:查看当前值 → 追加演示目录 → 新命令直接可用 → readonly 保护下追加被拒。演示脚本 /root/demo/export/bin/hello 在 2.1 节已备好:

$ echo PATH=$PATH
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

$ export PATH=$PATH:/root/demo/export/bin

$ hello
hello-from-demo-bin

$ readonly PATH

$ export PATH=$PATH:/tmp; echo rc=$?
/root/export-test.sh: line 3: PATH: readonly variable
rc=1

$ echo PATH-unmodified: $PATH
PATH-unmodified: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/demo/export/bin

解读:

  • export PATH=$PATH:/root/demo/export/bin 用 $PATH 引用旧值再拼接新目录(冒号分隔、新目录追加在尾部),随后 hello 无须写路径直接执行——命令查找按新 PATH 命中演示脚本;
  • readonly PATH 之后,任何修改 PATH 的尝试都报 PATH: readonly variable 且 rc=1,PATH-unmodified 输出证明值仍含此前追加的 /root/demo/export/bin;
  • 会话级 PATH 修改重启即失效,持久化写法见 4.4 节。

再看 LANG 与 HOME。LANG 在本环境默认未设置(空),导出赋值后当前 shell 与子进程同时生效:

$ echo LANG-before: [$LANG]
LANG-before: []

$ export LANG=C.UTF-8

$ echo LANG-after: [$LANG]
LANG-after: [C.UTF-8]

$ bash -c 'echo LANG-in-child: $LANG'
LANG-in-child: C.UTF-8

$ export -p | grep LANG
declare -x LANG="C.UTF-8"

$ date
Thu Oct 15 21:20:11 UTC 2026

HOME 一般无需手动导出(登录时已由 pam_env 等设置好),确认其值与导出状态即可:

$ echo HOME=$HOME
HOME=/root

💡 要点:① 追加 PATH 永远引用旧值 $PATH 再拼接,直接 export PATH=/root/bin 会把系统目录全挤掉,常见症状是 ls 都找不到;② LANG/C.UTF-8 是 Ubuntu 22.04 支持的默认 locale 值,程序按它决定语言与编码;③ 修改 PATH 前留一份原值(echo $PATH 记下),防手误。

四、实战示例

4.1 env 临时注入:只给一条命令加环境变量

env 变量=值 命令 可以在不污染当前 shell 的前提下,给这一条命令注入变量:

$ env DEMO_ENV=inline bash -c 'echo env-injected: $DEMO_ENV'
env-injected: inline

$ echo parent-saw: $DEMO_ENV; echo rc=$?
parent-saw:
rc=0

解读:

  • 子 bash 读到了 DEMO_ENV=inline(env 把它加进环境后再 exec bash);
  • 父 shell 的 echo $DEMO_ENV 为空——注入只作用于那一条命令,当前会话零残留;
  • 等价的 export 写法需要 export DEMO_ENV=inline + 用后 export -n DEMO_ENV 两步;一次性场景用 env 更干净。

💡 要点:env 与 export 的分工——改自己的会话用 export,改别人的进程用 env。常见如 env DEBUG=1 ./app.sh、env LANG=C sort file.txt。

4.2 env -i:干净环境启动

env -i(--ignore-environment)把环境整个清空再启动命令,用来观察"环境不是天生就有,而是逐层传下来的":

$ env -i bash -c 'echo PATH-in-clean-env: [$PATH]'
PATH-in-clean-env: [/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin]

$ env -i bash -c 'echo HOME-in-clean-env: [$HOME]'
HOME-in-clean-env: []

解读:

  • 干净环境里 HOME 为空——HOME 不是 bash 内建的,是登录流程注入环境的;
  • PATH 却"自动有了"——bash 启动时若发现 PATH 未设置,会按编译默认值(/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin)补上,这正是系统默认 PATH 的来源;
  • env -i bash 因此能复现"最小环境"问题:某些依赖 HOME/TMPDIR 的程序在 cron、systemd 等精简环境下失败,可用它本地复现排查。

4.3 启动脚本读取导出变量:配置注入的标准链路

"父 shell 导出 → 子脚本读取"是应用配置注入(如 APP_MODE、端口)最常用的链路。先创建读取环境变量的演示脚本:

$ printf '#!/bin/bash\necho APP_MODE-in-script: $APP_MODE\necho APP_PORT-in-script: $APP_PORT\n' > /root/demo/export/app.sh

$ chmod +x /root/demo/export/app.sh

$ export APP_MODE=production APP_PORT=8080

$ /root/demo/export/app.sh
APP_MODE-in-script: production
APP_PORT-in-script: 8080

解读:

  • export APP_MODE=production APP_PORT=8080 一次导出两个变量(3.5 节语法);
  • 直接执行 /root/demo/export/app.sh,脚本作为子 bash 进程从环境读到两个值并打印——脚本自身没有任何赋值语句,值全部来自外部注入;
  • 这条链路的好处:脚本与配置解耦,同一份脚本不改一行即可切换 APP_MODE=staging 等环境;这也是 12-factor 应用"配置走环境变量"的 shell 基础。

💡 要点:反向不成立——脚本里 APP_MODE=staging 改的只是子进程内自己的副本,退出后父 shell 无感(单向性,见五、Q5)。

4.4 持久化:export 写入 /root/.bashrc

会话级 export 重启即失效,持久化写进 /root/.bashrc(每个新交互 shell 启动时重新执行)。完整流程实测:追加 → 新 shell 验证 → 删除恢复(演示后清理,避免残留):

$ cp /root/.bashrc /root/.bashrc.export-bak

$ echo 'export APP_PERSIST=demo-value' >> /root/.bashrc

$ tail -1 /root/.bashrc
export APP_PERSIST=demo-value

$ bash -ic 'echo APP_PERSIST-in-new-shell: $APP_PERSIST' 2>/dev/null
APP_PERSIST-in-new-shell: demo-value

$ sed -i '/export APP_PERSIST=/d' /root/.bashrc

$ bash -ic 'echo APP_PERSIST-after-clean: [$APP_PERSIST]' 2>/dev/null
APP_PERSIST-after-clean: []

$ mv /root/.bashrc.export-bak /root/.bashrc

流程解读:

  • echo 'export APP_PERSIST=demo-value' >> /root/.bashrc 用单引号保证写入的是原样语句(若用双引号,当前 shell 会立刻展开 $APP_PERSIST);
  • bash -ic '...' 模拟"新开一个交互 shell":加载 .bashrc 执行其中的 export,变量生效,输出 demo-value(2>/dev/null 丢弃该场景的作业控制提示);
  • 删除该行后新 shell 中变量为空([]),证明来源就是 .bashrc 里的这一行 export;
  • 对当前已打开的终端,改动 .bashrc 后需要 source ~/.bashrc 立即生效。

4.5 管道与子 shell 赋值不回传陷阱:实测与三种修正

经典陷阱:管道右侧的命令在子 shell 中执行,里面的赋值随子 shell 结束而丢弃。先看两种"赋值丢失"的实测形态:

$ echo 'hello world' | read A B

$ echo A=$A B=$B
A= B=

$ printf 'line1\nline2\n' > /root/demo/export/data.txt

$ COUNT=0

$ cat /root/demo/export/data.txt | while read line; do COUNT=$((COUNT+1)); done

$ echo COUNT-after-pipe: $COUNT
COUNT-after-pipe: 0

read A B 读到了数据但 A、B 在子 shell 里;while 循环跑了 2 行但 COUNT 仍为 0。修正一:here-string 把数据喂给当前 shell 的 read:

$ read A B <<< 'hello world'

$ echo A=$A B=$B
A=hello B=world

修正二:输入重定向替代管道,让 while 留在当前 shell:

$ while read line; do COUNT=$((COUNT+1)); done < /root/demo/export/data.txt

$ echo COUNT-after-redirect: $COUNT
COUNT-after-redirect: 2

修正三:shopt -s lastpipe(bash 4.2+,须在非交互 shell 或关闭作业控制时生效),让管道末端的 while 回到当前 shell 执行:

$ shopt -s lastpipe

$ COUNT2=0

$ printf 'x\ny\nz\n' | while read line; do COUNT2=$((COUNT2+1)); done

$ echo COUNT2-after-lastpipe: $COUNT2
COUNT2-after-lastpipe: 3

💡 要点:这与 export 的关系在于方向感——管道、子 shell、子进程都是单向边界,export 解决"往子进程传"的问题,而这里要解决的是"子 shell 的修改传不回来",两者都不能越界回传;回传需求的答案是 here-string、重定向、lastpipe 或进程替换。交互 shell 中 lastpipe 默认无效(作业控制开启),交互环境优先用 here-string。

五、注意事项与常见问题

Q1:明明 export 了,为什么子进程/新终端里还是看不到? 分两种方向排查:① 子进程看不到——用 export -p | grep 变量名 确认当前 shell 侧确有导出(无命中说明没导出或已被 -n 收回,见 3.6 节),有命中则检查子进程启动链路是否重置了环境(如 4.2 节 env -i、systemd 的 Environment= 覆盖);② 新终端看不到——export 只作用于当前会话及其子进程,新终端是全新会话,必须把 export 写进 ~/.bashrc(4.4 节实测验证)。

Q2:export 能把子进程里的赋值"传回"父 shell 吗? 不能。环境传递是单向向下的:父进程 exec 子进程时复制环境块,子进程怎么改都与父进程无关。实测形态:bash -c 'VAR=in-child' 后父 shell 的 $VAR 仍为空(环境块复制语义,3.3 节同源)。想让"子进程的计算结果"回到父 shell,用命令替换 RESULT=$(子命令)、source 脚本(在当前 shell 执行而非子进程),或写入临时文件再读回。

Q3:脚本里 export 了变量,为什么后续 cron/服务里还是没有? export 的生命周期止于当前 shell 进程——脚本退出即消失,cron 是独立会话什么也继承不到(除非在同一脚本内启动服务进程)。正确姿势:把 export 写入服务单元(systemd 的 Environment=/EnvironmentFile=)、cron 任务头部,或包一层启动脚本在 exec 服务进程前 export。

Q4:env、printenv、export -p 三者怎么选? 记忆口诀:printenv 查值、export -p 查导出状态、env 改环境。printenv 名字 最快确认子进程视角的单个变量(3.7 节);export -p 输出可回放、含空值导出名,适合快照比对(3.1 节);env VAR=x cmd 临时注入、env -i 干净启动(4.1/4.2 节)。env 与 printenv 是外部命令,export 是 bash 内置——在纯 sh 脚本里 export 语法可能不兼容(如不支持 export -f)。

Q5:export 一个 readonly 变量会怎样? 分两种:export 只读变量=新值 报 readonly variable、rc=1,赋值被拒(3.9 节 RO_VAR/PATH 实测);不带赋值的 export 只读变量 合法,只是补打导出标记。想让变量"有值 + 只读 + 导出",按 export VAR=值 → readonly VAR 顺序操作。

Q6:echo $VAR 与 "$VAR" 什么时候必须加双引号? 值含空格、通配符时必须加:echo "$APP_MODE" 保证作为一个整体参数,防止词拆分与路径展开。4.3 节演示脚本中 echo APP_MODE-in-script: $APP_MODE 未加引号是因其值无空格;生产脚本建议统一加引号。export 本身不受影响(赋值时引号规则相同:export MSG="hello world" 引号包住整体)。

六、参考链接