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 赋值不回传陷阱及修正。
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 |
| Shell | GNU 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 -p | bash 内置 | declare -x NAME="value" | 配合 grep | 可回放的导出状态快照,含值为空的导出名 |
env | 外部命令 /usr/bin/env | NAME=value | 否(配合 grep) | 可临时注入变量启动命令(4.1 节)、env -i 清空环境(4.2 节) |
printenv | 外部命令 /usr/bin/printenv | NAME=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" 引号包住整体)。
六、参考链接
- Bash Reference Manual — Bourne Shell Builtins(export):https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Builtins.html
- Bash Reference Manual — Shell Parameters(export 属性与变量导出语义):https://www.gnu.org/software/bash/manual/html_node/Shell-Parameters.html
- man7.org bash(1) 手册页(ENVIRONMENT 一节):https://man7.org/linux/man-pages/man1/bash.1.html
- man7.org environ(7)(进程环境块语义):https://man7.org/linux/man-pages/man7/environ.7.html
- man7.org env(1):https://man7.org/linux/man-pages/man1/env.1.html
- Ubuntu 手册页 bash(1)(Ubuntu 22.04 manpages.ubuntu.com):https://manpages.ubuntu.com/manpages/jammy/en/man1/bash.1.html