Advanced Bash-Scripting Guide: An in-depth exploration of the art of shell scripting
Prev | Chapter 24. Functions | Next

# 24.2. Local Variables

What makes a variable local?

local variables

  A variable declared as local is one that is visible only within the block of code in which it appears. It has local scope. In a function, a local variable has meaning only within that function block. [1]

  Example 24-12. Local variable visibility

         1 #!/bin/bash
         2 # ex62.sh: Global and local variables inside a function.
         3
         4 func ()
         5 {
         6   local loc_var=23       # Declared as local variable.
         7   echo                   # Uses the 'local' builtin.
         8   echo "\"loc_var\" in function = $loc_var"
         9   global_var=999         # Not declared as local.
        10                          # Therefore, defaults to global.
        11   echo "\"global_var\" in function = $global_var"
        12 }
        13
        14 func
        15
        16 # Now, to see if local variable "loc_var" exists outside the function.
        17
        18 echo
        19 echo "\"loc_var\" outside function = $loc_var"
        20                                       # $loc_var outside function =
        21                                       # No, $loc_var not visible globally.
        22 echo "\"global_var\" outside function = $global_var"
        23                                       # $global_var outside function = 999
        24                                       # $global_var is visible globally.
        25 echo
        26
        27 exit 0
        28 #  In contrast to C, a Bash variable declared inside a function
        29 #+ is local ONLY if declared as such.

  Before a function is called, all variables declared within the function are invisible outside the body of the function, not just those explicitly declared as local.

         1 #!/bin/bash
         2
         3 func ()
         4 {
         5 global_var=37    #  Visible only within the function block
         6                  #+ before the function has been called.
         7 }                #  END OF FUNCTION
         8
         9 echo "global_var = $global_var"  # global_var =
        10                                  #  Function "func" has not yet been called,
        11                                  #+ so $global_var is not visible here.
        12
        13 func
        14 echo "global_var = $global_var"  # global_var = 37
        15                                  # Has been set by function call.

  As Evgeniy Ivanov points out, when declaring and setting a local variable in a single command, apparently the order of operations is to first set the variable, and only afterwards restrict it to local scope. This is reflected in the return value.

         1 #!/bin/bash
         2
         3 echo "==OUTSIDE Function (global)=="
         4 t=$(exit 1)
         5 echo $?      # 1
         6              # As expected.
         7 echo
         8
         9 function0 ()
        10 {
        11
        12 echo "==INSIDE Function=="
        13 echo "Global"
        14 t0=$(exit 1)
        15 echo $?      # 1
        16              # As expected.
        17
        18 echo
        19 echo "Local declared & assigned in same command."
        20 local t1=$(exit 1)
        21 echo $?      # 0
        22              # Unexpected!
        23 #  Apparently, the variable assignment takes place before
        24 #+ the local declaration.
        25 #+ The return value is for the latter.
        26
        27 echo
        28 echo "Local declared, then assigned (separate commands)."
        29 local t2
        30 t2=$(exit 1)
        31 echo $?      # 1
        32              # As expected.
        33
        34 }
        35
        36 function0

## 24.2.1. Local variables and recursion.

Local variables are a useful tool for writing recursive code, but this practice generally involves a great deal of computational overhead and is definitely not recommended in a shell script. [6]

Example 24-15. Recursion, using a local variable

       1 #!/bin/bash
       2
       3 #               factorial
       4 #               ---------
       5
       6
       7 # Does bash permit recursion?
       8 # Well, yes, but...
       9 # It's so slow that you gotta have rocks in your head to try it.
      10
      11
      12 MAX_ARG=5
      13 E_WRONG_ARGS=85
      14 E_RANGE_ERR=86
      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 ($MAX_ARG is maximum)."
      26   #  Let's get real now.
      27   #  If you want greater range than this,
      28   #+ rewrite it in a Real Programming Language.
      29   exit $E_RANGE_ERR
      30 fi
      31
      32 fact ()
      33 {
      34   local number=$1
      35   #  Variable "number" must be declared as local,
      36   #+ otherwise this doesn't work.
      37   if [ "$number" -eq 0 ]
      38   then
      39     factorial=1    # Factorial of 0 = 1.
      40   else
      41     let "decrnum = number - 1"
      42     fact $decrnum  # Recursive function call (the function calls itself).
      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

Also see Example A-15 for an example of recursion in a script. Be aware that recursion is resource-intensive and executes slowly, and is therefore generally not appropriate in a script.

### Notes

[1]

However, as Thomas Braunberger points out, a local variable declared in a function is also visible to functions called by the parent function.

       1 #!/bin/bash
       2
       3 function1 ()
       4 {
       5   local func1var=20
       6
       7   echo "Within function1, \$func1var = $func1var."
       8
       9   function2
      10 }
      11
      12 function2 ()
      13 {
      14   echo "Within function2, \$func1var = $func1var."
      15 }
      16
      17 function1
      18
      19 exit 0
      20
      21
      22 # Output of the script:
      23
      24 # Within function1, $func1var = 20.
      25 # Within function2, $func1var = 20.

This is documented in the Bash manual:

"Local can only be used within a function; it makes the variable name have a visible scope restricted to that function and its children." [emphasis added] The ABS Guide author considers this behavior to be a bug.

[2] | Otherwise known as redundancy.
[3] | Otherwise known as tautology.
[4] | Otherwise known as a metaphor.
[5] | Otherwise known as a recursive function.

[6]

Too many levels of recursion may crash a script with a segfault.

       1 #!/bin/bash
       2
       3 #  Warning: Running this script could possibly lock up your system!
       4 #  If you're lucky, it will segfault before using up all available memory.
       5
       6 recursive_function ()
       7 {
       8 echo "$1"     # Makes the function do something, and hastens the segfault.
       9 (( $1 < $2 )) && recursive_function $(( $1 + 1 )) $2;
      10 #  As long as 1st parameter is less than 2nd,
      11 #+ increment 1st and recurse.
      12 }
      13
      14 recursive_function 1 50000  # Recurse 50,000 levels!
      15 #  Most likely segfaults (depending on stack size, set by ulimit -m).
      16
      17 #  Recursion this deep might cause even a C program to segfault,
      18 #+ by using up all the memory allotted to the stack.
      19
      20
      21 echo "This will probably not print."
      22 exit 0  # This script will not exit normally.
      23
      24 #  Thanks, Stéphane Chazelas.

Prev | Home | Next
Functions | Up | Recursion Without Local Variables