Bash – Scope of Local Variables in Shell Functions

bashfunctionshellshell-scriptvariable

After reading 24.2. Local Variables, I thought that declaring a variable var with the keyword local meant that var's value was only accessible within the block of code delimited by the curly braces of a function.

However, after running the following example, I found out that var can also be accessed, read and written from the functions invoked by that block of code — i.e. even though var is declared local to outerFunc, innerFunc is still able to read it and alter its value.

Run It Online

#!/usr/bin/env bash

function innerFunc() {
    var='new value'
    echo "innerFunc:                   [var:${var}]"
}

function outerFunc() {
    local var='initial value'

    echo "outerFunc: before innerFunc: [var:${var}]"
    innerFunc
    echo "outerFunc: after  innerFunc: [var:${var}]"
}

echo "global:    before outerFunc: [var:${var}]"
outerFunc
echo "global:    after  outerFunc: [var:${var}]"

Output:

global:    before outerFunc: [var:]               # as expected, `var` is not accessible outside of `outerFunc`
outerFunc: before innerFunc: [var:initial value]
innerFunc:                   [var:new value]      # `innerFunc` has access to `var` ??
outerFunc: after  innerFunc: [var:new value]      # the modification of `var` by `innerFunc` is visible to `outerFunc` ??
global:    after  outerFunc: [var:]

Q: Is that a bug in my shell (bash 4.3.42, Ubuntu 16.04, 64bit) or is it the expected behavior ?

EDIT: Solved. As noted by @MarkPlotnick, this is indeed the expected behavior.

Best Answer

Shell variables have a dynamic scope. If a variable is declared as local to a function, that scope remains until the function returns, including during calls to other functions.

There are two exceptions:

  1. in ksh93, if a function is defined with the standard function_name () { … } syntax, then its local variables obey dynamic scoping. But if a function is defined with the ksh syntax function function_name { … } then its local variable obey lexical/static scoping, so they are not visible in other functions called by this.

  2. the zsh/private autoloadable plugin in zsh provides with a private keyword/builtin which can be used to declare a variable with static scope.

ash, bash, pdksh and derivatives, bosh only have dynamic scoping.

Related Question