变量作用域 #

一、变量作用域概述 #

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