变量作用域 #
一、变量作用域概述 #
1.1 作用域类型 #
Shell中的变量作用域分为三种:
| 类型 | 关键字 | 作用范围 |
|---|---|---|
| 全局变量 | 无 | 整个脚本 |
| 局部变量 | local | 当前函数 |
| 环境变量 | export | 当前进程及子进程 |
1.2 基本示例 #
bash
#!/bin/bash
# 全局变量
global_var="全局变量"
my_function() {
# 局部变量
local local_var="局部变量"
echo "函数内 - 全局: $global_var"
echo "函数内 - 局部: $local_var"
}
my_function
echo "函数外 - 全局: $global_var"
echo "函数外 - 局部: $local_var" # 空值
二、全局变量 #
2.1 默认作用域 #
bash
#!/bin/bash
# 默认定义的变量是全局的
counter=0
increment() {
((counter++))
echo "函数内: $counter"
}
increment # 输出: 1
increment # 输出: 2
echo "函数外: $counter" # 输出: 2
2.2 函数内修改全局变量 #
bash
#!/bin/bash
name="张三"
change_name() {
name="李四" # 修改全局变量
echo "函数内: $name"
}
echo "调用前: $name" # 输出: 张三
change_name # 输出: 李四
echo "调用后: $name" # 输出: 李四
2.3 全局变量的陷阱 #
bash
#!/bin/bash
# 意外修改全局变量
count=10
process() {
count=0 # 意外修改了全局变量
for i in {1..5}; do
((count++))
done
echo "处理结果: $count"
}
echo "调用前: $count" # 输出: 10
process # 输出: 5
echo "调用后: $count" # 输出: 5(被意外修改)
三、局部变量 #
3.1 使用local关键字 #
bash
#!/bin/bash
# 使用local定义局部变量
process() {
local count=0
for i in {1..5}; do
((count++))
done
echo "函数内: $count"
}
count=10
echo "调用前: $count" # 输出: 10
process # 输出: 5
echo "调用后: $count" # 输出: 10(未受影响)
3.2 局部变量遮蔽 #
bash
#!/bin/bash
name="全局张三"
show_name() {
local name="局部李四" # 遮蔽全局变量
echo "函数内: $name"
}
show_name # 输出: 局部李四
echo "函数外: $name" # 输出: 全局张三
3.3 局部变量的作用范围 #
bash
#!/bin/bash
outer() {
local var="外部函数"
inner() {
local var="内部函数"
echo "内部函数: $var"
}
echo "外部函数 - 调用前: $var"
inner
echo "外部函数 - 调用后: $var"
}
outer
四、环境变量 #
4.1 使用export #
bash
#!/bin/bash
# 普通变量不会传递给子进程
normal_var="普通变量"
export env_var="环境变量"
# 子进程
bash -c 'echo "普通变量: $normal_var"' # 空
bash -c 'echo "环境变量: $env_var"' # 输出: 环境变量
4.2 在函数中使用export #
bash
#!/bin/bash
set_env() {
export MY_VAR="环境变量"
local local_var="局部变量"
}
set_env
echo "MY_VAR: $MY_VAR" # 输出: 环境变量
echo "local_var: $local_var" # 空
# 子进程可以访问
bash -c 'echo "子进程: $MY_VAR"'
4.3 环境变量的生命周期 #
bash
#!/bin/bash
# export只在当前Shell会话有效
export TEMP_VAR="临时变量"
# 子脚本可以访问
./child_script.sh
# 新的Shell会话无法访问
# bash # 新会话
# echo $TEMP_VAR # 空
五、作用域实战 #
5.1 配置管理 #
bash
#!/bin/bash
# 全局配置
declare -g CONFIG_DIR="/etc/myapp"
declare -g LOG_FILE="/var/log/myapp.log"
load_config() {
local config_file="$1"
if [[ ! -f "$config_file" ]]; then
echo "配置文件不存在: $config_file" >&2
return 1
fi
# 局部变量读取配置
local key value
while IFS='=' read -r key value; do
[[ "$key" =~ ^#.*$ ]] && continue
[[ -z "$key" ]] && continue
# 设置全局配置
declare -g "CONFIG_${key^^}=$value"
done < "$config_file"
}
load_config "app.conf"
echo "数据库: $CONFIG_DATABASE"
5.2 计数器模式 #
bash
#!/bin/bash
# 使用全局计数器
TOTAL=0
SUCCESS=0
FAILED=0
process_file() {
local file="$1"
((TOTAL++))
if some_operation "$file"; then
((SUCCESS++))
return 0
else
((FAILED++))
return 1
fi
}
# 处理多个文件
for file in *.txt; do
process_file "$file"
done
echo "总计: $TOTAL, 成功: $SUCCESS, 失败: $FAILED"
5.3 递归函数 #
bash
#!/bin/bash
# 递归计算阶乘
factorial() {
local n=$1
if (( n <= 1 )); then
echo 1
return
fi
local prev=$(factorial $((n - 1)))
echo $((n * prev))
}
result=$(factorial 5)
echo "5! = $result"
# 使用局部变量避免递归问题
fibonacci() {
local n=$1
if (( n <= 1 )); then
echo $n
return
fi
local a=$(fibonacci $((n - 1)))
local b=$(fibonacci $((n - 2)))
echo $((a + b))
}
result=$(fibonacci 10)
echo "F(10) = $result"
5.4 函数库模式 #
bash
#!/bin/bash
# 函数库:utils.sh
# 私有变量(使用下划线前缀)
declare -g _utils_initialized=false
declare -g _utils_log_level=1
# 初始化函数
utils_init() {
if [[ "$_utils_initialized" == true ]]; then
return 0
fi
_utils_initialized=true
_utils_log_level="${LOG_LEVEL:-1}"
}
# 日志函数
utils_log() {
local level="$1"
shift
local message="$*"
utils_init
local level_num
case "$level" in
DEBUG) level_num=0 ;;
INFO) level_num=1 ;;
WARN) level_num=2 ;;
ERROR) level_num=3 ;;
esac
if (( level_num >= _utils_log_level )); then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $message"
fi
}
六、作用域最佳实践 #
6.1 变量命名规范 #
bash
#!/bin/bash
# 全局变量:大写字母
DATABASE_HOST="localhost"
LOG_LEVEL=1
# 局部变量:小写字母
process_data() {
local input_file="$1"
local output_file="$2"
local temp_file="/tmp/temp_$$"
# 处理逻辑
}
# 私有变量:下划线前缀
_internal_state="private"
6.2 避免全局变量污染 #
bash
#!/bin/bash
# 不推荐:使用全局变量
result=""
calculate() {
result=$(($1 + $2))
}
calculate 5 3
echo $result
# 推荐:使用返回值
calculate() {
echo $(($1 + $2))
}
result=$(calculate 5 3)
echo $result
6.3 使用declare明确作用域 #
bash
#!/bin/bash
# 明确声明全局变量
declare -g GLOBAL_VAR="全局"
my_function() {
# 明确声明局部变量
declare local_var="局部"
# 声明只读变量
declare -r CONSTANT="常量"
# 声明整数变量
declare -i count=0
}
七、作用域速查 #
7.1 变量声明 #
| 声明方式 | 作用域 | 示例 |
|---|---|---|
| var=value | 全局 | name=“张三” |
| local var=value | 函数局部 | local name=“张三” |
| export var=value | 环境变量 | export PATH=$PATH:/new |
| declare -g var=value | 全局 | declare -g name=“张三” |
7.2 作用域规则 #
| 规则 | 说明 |
|---|---|
| 默认全局 | 不使用local的变量是全局的 |
| local遮蔽 | 局部变量遮蔽同名全局变量 |
| export继承 | 环境变量被子进程继承 |
| 函数结束 | 局部变量在函数结束时销毁 |
八、总结 #
8.1 作用域要点 #
| 要点 | 说明 |
|---|---|
| 全局变量 | 默认作用域,整个脚本可见 |
| 局部变量 | 使用local,只在函数内可见 |
| 环境变量 | 使用export,子进程可继承 |
| 最佳实践 | 优先使用局部变量 |
8.2 下一步 #
你已经掌握了变量作用域,接下来让我们学习 递归函数,了解递归调用的实现!
最后更新:2026-03-27