未export的变量子shell不可见;仅export后的环境变量可被子shell继承,命令替换、圆括号等隐式子shell例外可访问父shell未export变量。

变量没导出,子 Shell 自然看不到
在父 Shell 中直接写 name="alice",这个变量只属于当前进程,不会自动传给任何子进程。子 Shell 启动时,只会继承 已 export 的变量。如果漏了 export,哪怕变量名拼对、路径写全,子 Shell 里 echo $name 也一定为空。
常见写法误区:
- export name="alice" ✅ 正确:声明+导出一步到位
- name="alice"; export name ✅ 正确:分两步也行
- name="alice"; export NAME ❌ 错误:大小写不一致,NAME 未定义
- export "name=alice" ❌ 错误:带引号会当成一个字符串字面量,不是变量赋值
脚本执行方式决定变量能否“回来”
用 ./script.sh 运行脚本,本质是启动一个子 Shell;里面所有变量赋值、export 操作,生命周期仅限于该子进程。脚本结束,变量就消失,父 Shell 完全无感。
想让变量影响当前终端环境,必须用 source script.sh 或 . script.sh ——它们是在当前 Shell 中逐行执行,不是另起炉灶。
典型误操作场景:
- 写好 export JAVA_HOME=/opt/jdk 存进 jdk.sh,然后运行 ./jdk.sh → java -version 仍报错
- 正确做法是 source jdk.sh,再验证 $JAVA_HOME 是否生效
- 如果要全局永久生效,得把 export 行写进 ~/.bashrc(交互式终端)或 ~/.profile(登录 Shell),并确保文件被加载
小括号 () 和管道 | 是隐形子 Shell 触发器
很多人以为只有显式调用 bash 或 ./ 才会进子 Shell,其实 ()、|、$()、& 都会自动 fork 出子 Shell。一旦变量在这些结构里被修改,父 Shell 就收不到更新。
例如这段代码:
count=0 ls /tmp | while read f; do count=$((count + 1)) done echo $count # 输出 0,不是实际文件数
原因:管道右侧的 while 在子 Shell 中执行,count 的自增只发生在子 Shell 内部。
绕过方法:
- 用重定向替代管道:while read f; do count=$((count + 1)); done
- 用 here-string 或 process substitution 控制执行上下文
- 把整个逻辑包进函数,避免在子 Shell 中修改关键变量
PATH 覆盖错误导致命令“突然消失”
修改 PATH 时写成 export PATH="/my/tool/bin",等于彻底丢掉系统原有路径,之后 ls、cp、echo 全都找不到——因为这些命令不在 /my/tool/bin 下。
正确做法永远是追加或前置:
- export PATH="/my/tool/bin:$PATH" → 新路径优先查找
- export PATH="$PATH:/my/tool/bin" → 新路径放末尾
- 临时恢复系统默认 PATH(当 PATH 崩溃时):export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
验证是否生效:用 export -p | grep PATH 或 echo $PATH,注意输出中是否有预期路径且顺序合理。











