Bash中变量默认全局,函数内需用local声明局部变量,子shell和管道中修改不影响父shell,跨脚本共享需source或export。

shell 脚本中变量默认是全局的,没有块级作用域
在 Bash 中写 if、for、while 甚至函数内部直接赋值的变量,除非显式声明,否则一律是全局可读写的。这点和 Python 或 JavaScript 差异极大,容易误以为“缩进/大括号内定义的变量是局部的”。
-
for i in 1 2 3; do x=$i; done执行完后x仍存在,值为3 -
if true; then y=hello; fi后y可直接echo $y - 子 shell(如管道右侧、
(...))里修改变量,父 shell 看不到 —— 这是进程隔离,不是作用域限制
用 local 声明函数内局部变量,但仅限函数体内
local 必须出现在函数定义内部、且必须在首次使用前声明,否则无效。它只对当前函数生效,嵌套函数不自动继承,也不能在顶层(非函数上下文)使用,否则报错 local: not in a function。
- 正确:
myfunc() { local tmp="inside" echo "$tmp" } myfunc # 输出 inside echo "$tmp" # 空,未定义 - 错误:
local bad="here" # 报错:local: not in a function
- 注意:
local不会覆盖同名全局变量的值,但会屏蔽其读取 —— 函数内读不到外层同名变量,除非用declare -g
子 shell(()、管道、命令替换)会复制变量,但修改不回传
用 (...) 启动子 shell,或在管道右侧、$(...) 中执行命令,都会 fork 新进程。此时变量被复制一份,改了不影响父 shell 的原始值。
(counter=5; echo "in sub: $counter") # 输出 5 echo "outside: $counter" # 仍是原值(空或旧值)
- 管道尤其隐蔽:
echo "a b c" | while read word; do buf+="$word "; done; echo "$buf" # 输出空 —— while 在子 shell 中
- 绕过方法:用重定向替代管道,或把逻辑包进函数并用
declare -g显式写全局变量(慎用)
跨脚本变量传递靠 source 或 export,别混淆
两个脚本间共享变量,不能靠“运行另一个脚本”实现 —— ./other.sh 是子进程,改了变量父进程看不到。必须用 source other.sh(或 . other.sh)在当前 shell 加载;若需让子进程继承,则用 export VAR=value。
-
export后变量成为环境变量,所有后续子进程(如ls、python)都能读到,但无法反向影响父进程 -
source是解析执行,相当于把文件内容粘贴到当前上下文,变量直接生效,无进程开销 - 常见错误:在脚本开头写
export PATH=$PATH:/my/bin,却忘了这行只影响该脚本启动的子进程,不会改变当前终端的PATH
变量作用域的边界其实就三条线:函数体、子 shell 进程、是否 export。Bash 没有词法作用域,也没有自动局部化,一切依赖显式声明和进程模型。写复杂脚本时,漏掉 local 或误信管道能改变量,是最常卡住人的地方。










