23.2. 局部变量

怎么样使一个变量变成局部的?

局部变量

如果变量用local来声明,那么它只能在该变量声明的代码块(block of code)中可见. 这个代码块就是局部"范围". 在一个函数内,局部变量意味着只能在函数代码块内它才有意义.


例子 23-12. 局部变量的可见范围

   1 #!/bin/bash
   2 # 在函数内部的全局和局部变量.
   3 
   4 func ()
   5 {
   6   local loc_var=23       # 声明为局部变量.
   7   echo                   # 使用内建的'local'关键字.
   8   echo "\"loc_var\" in function = $loc_var"
   9   global_var=999         # 没有声明为局部变量.
  10                          # 默认为全局变量. 
  11   echo "\"global_var\" in function = $global_var"
  12 }  
  13 
  14 func
  15 
  16 # 现在,来看看是否局部变量"loc_var"能否在函数外面可见.
  17 
  18 echo
  19 echo "\"loc_var\" outside function = $loc_var"
  20                                       # $loc_var outside function = 
  21                                       # 不, $loc_var不是全局可访问的.
  22 echo "\"global_var\" outside function = $global_var"
  23                                       # $global_var outside function = 999
  24                                       # $global_var 是全局可访问的.
  25 echo				      
  26 
  27 exit 0
  28 #  与In contrast to C相比, 在函数内声明的Bash变量只有在
  29 #+ 它被明确声明成局部的变量时才是局部的.

Caution

在函数调用之前,所有在函数内声明且没有明确声明为local的变量都可在函数体外可见.
   1 #!/bin/bash
   2 
   3 func ()
   4 {
   5 global_var=37    #  在函数还没有被调用前
   6                  #+ 变量只在函数内可见. 
   7 }                #  函数结束
   8 
   9 echo "global_var = $global_var"  # global_var =
  10                                  #  函数"func"还没有被调用,
  11                                  #+ 所以变量$global_var还不能被访问.
  12 
  13 func
  14 echo "global_var = $global_var"  # global_var = 37
  15                                  # 已经在函数调用时设置了值.

23.2.1. 局部变量使递归变得可能.

局部变量可以递归, [1] 但这个办法会产生大量的计算,因此它在shell脚本中是被明确表明不推荐的. [2]


例子 23-13. 用局部变量来递归

   1 #!/bin/bash
   2 
   3 #               阶乘
   4 #               ---------
   5 
   6 
   7 # bash允许递归吗?
   8 # 嗯, 允许, 但是...
   9 # 它太慢以致你难以忍受.
  10 
  11 
  12 MAX_ARG=5
  13 E_WRONG_ARGS=65
  14 E_RANGE_ERR=66
  15 
  16 
  17 if [ -z "$1" ]
  18 then
  19   echo "Usage: `basename $0` number"
  20   exit $E_WRONG_ARGS
  21 fi
  22 
  23 if [ "$1" -gt $MAX_ARG ]
  24 then
  25   echo "Out of range (5 is maximum)."
  26   #  现在让我们来了解实际情况.
  27   #  如果你想求比这个更大的范围的阶乘,
  28   #+ 应该重新用一个真正的编程语言来写.
  29   exit $E_RANGE_ERR
  30 fi  
  31 
  32 fact ()
  33 {
  34   local number=$1
  35   #  变量"number"必须声明为局部,
  36   #+ 否则它不会工作.
  37   if [ "$number" -eq 0 ]
  38   then
  39     factorial=1    # 0的阶乘为1.
  40   else
  41     let "decrnum = number - 1"
  42     fact $decrnum  # 递归调用(函数内部调用自己本身).
  43     let "factorial = $number * $?"
  44   fi
  45 
  46   return $factorial
  47 }
  48 
  49 fact $1
  50 echo "Factorial of $1 is $?."
  51 
  52 exit 0

也请参考例子 A-16的脚本递归的例子. 必须意识到递归也意味着巨大的资源消耗和缓慢的运行,因此它不适合在脚本中使用.

[1]

Herbert Mayer 给递归下的定义是". . . expressing an algorithm by using a simpler version of that same algorithm(用一个相同算法的版本来表示一个算法) . . ." 递归函数是调用它自己本身的函数.

[2]

太多层的递归可能会引起脚本段错误而崩溃.
   1 #!/bin/bash
   2 
   3 #  警告: 运行这个脚本可能使你的系统失去响应!
   4 #  如果你运气不错,在它使用完所有可用内存之前会段错误而退出.
   5 
   6 recursive_function ()		   
   7 {
   8 echo "$1"     # 使函数做些事情以加速产生段错误.
   9 (( $1 < $2 )) && recursive_function $(( $1 + 1 )) $2;
  10 #  当第一个参数比第二个参数少时,
  11 #+ 把第1个参数增1再次递归.
  12 }
  13 
  14 recursive_function 1 50000  # 递归 50,000 次!
  15 #  非常可能段错误 (依赖于栈的大小,它由ulimit -m设置).
  16 
  17 #  这种深度的递归甚至可能由于耗尽栈的内存大小而引起C程序的段错误.
  18 #
  19 
  20 
  21 echo "This will probably not print."
  22 exit 0  # 这个脚本将不会从这儿正常退出.
  23 
  24 #  多谢, St閜hane Chazelas.