一 函数调用时, 局部变量的”穿透”

一 函数调用时, 局部变量的”穿透”

本文讨论 linux shell 脚本函数中, 使用 local 定义的变量的 “穿透性”

先看代码:

    way1(){
        funA(){
            local var_a="hello funA"
            funB
            echo $var_a
        }
        funB(){
            var_a="modified by funB"
        }
        
        funA
        echo "global: <$var_a>"
    }

    way1

运行结果:

in funB, var_a=
modified by funB
global: <>

说明, 在被调用函数内部, 可以自由访问(读写)上级函数定义的局部变量. 在 funA 调用结束, 打印 $var_a 为空, 说明 funB 修改的的确不是全局变量.

那这有什么作用呢?

仅仅这样使用上级函数的变量, 缺乏灵活性, 我们需要的是”动态”, 演变成所谓的”输出参数”, 例如:

    way2(){
        funA(){
            local va="hello, funA"
            funB va # 注意不是 $va
            echo "result:{$va}"
        }
        
        # $1: 要修改的变量名
        funB(){
            local v_name=$1
            eval "$v_name=\"modified bu funB\""
        }
        funA
    }
    way2

运行结果:

result:{modified bu funB}

说明, 我们成功实现了输出参数. 类似于”指针”, 这在一些特定的场景, 特别是无法使用 echo printf 返回值, 或者是必须回避创建子 shell等情况下, 可能会起到出奇制胜的效果.

注意使用 eval 赋值时, 两侧双引号的转义斜杆是必须的.

【分析】

所谓 “局部变量” 的穿透, 本质上是同一进程中变量的共享, 所以, 如果上述的 funB 调用, 创建了子 shell, 默认是不穿透的, 例如, 为了 funB 的返回值, 不得不采用的调用形式: $(funB va)

    way3(){
        funA(){
            g_var="global var in funA"
            local va="hello, funA"
            local temp=$(funB va) # 注意不是 $va
            echo "result:{$va}, temp={$temp}"
        }

        # $1: 要修改的变量名
        funB(){
            g_var="no, this is in funB"
            local v_name=$1
            eval "$v_name=\"modified bu funB\""
            echo 100
        }
        funA
        echo "global: <$var_a>, g_var=<$g_var>"
    }
    way3

测试结果:

即无法修改上级函数中的局部变量 va, 其实, 即使上级函数中定义的全局变量(g_var)也无法修改, 因为两个 shell 是隔离的.