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}]"
    echo "outerFunc: after  innerFunc: [var:${var}]"

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


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.

  • It is the expected behavior – fpmurphy May 11 '16 at 17:09
  • 2
    Am I the only one who thinks it's weird that on the last line of output the value of var is empty? var is set globally in innerFunc, so why doesn't it stick until the end of the script? – Harold Fischer Feb 5 '19 at 3:42

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.

  • Do all variables in the shell have a dynamic scope or does this only apply to variables declared with local? – Harold Fischer Jan 26 '19 at 0:56
  • @HaroldFischer All variables have dynamic scope. With a typeset or declare or local declaration, the scope is until the function returns. Without such a declaration, the scope is global. – Gilles 'SO- stop being evil' Jan 26 '19 at 14:46
  • 1
    IMHO, If a variable is declared as local to a function, that scope remains until the function returns. is not enough to explain what is dynamic scope verus lexical scope. The description alone is also applied to lexical scope. – jinbeom hong Jun 14 '20 at 18:18
  • 2
    @jinbeomhong No, with lexical scope, a variable from a function is not visible while this function calls another function. I've added a sentence to state this explicitly. – Gilles 'SO- stop being evil' Jun 15 '20 at 7:29
  • Does this also affect function calling builtins? Or does builtins have their own scope? – Franklin Yu Nov 30 '20 at 8:23

It isn't a bug, the call inside the context of the outerFunc uses that local copy of $var. The "local" in outerFunc means the global isn't changed. If you call innerFunc outside of outerFunc, then there will be a change to the global $var, but not the outerFunc's local $var. If you added "local" to innerFunc, then outerFunc's $var wouldn't be changed - in essence, there'd be 3 of them:

  • $global::var
  • $outerFunc::var
  • $innerFunc::var

to use Perl's namespace format, sort of.


In function innerFunc() the var='new value' wasn't declared as local, therefore it's available in visible scope (once the function has been called).

Conversely, in function outerFunc() the local var='initial value' was declared as local, therefore it's not available in the global scope (even if the function has been called).

Because innerFunc() was called as a child of outerFunc(), var is within the the local scope of outerFunc().

man 1 bash may help clarify

local [option] [name[=value] ...]

For each argument, a local variable named name is created, and assigned value. The option can be any of the options accepted by declare. When local is used within a function, it causes the variable name to have a visible scope restricted to that function and its children. ...

The implied behavior that's expected in the description could be achieved by declaring local var='new value in function innerFunc().

As others have stated, this is not a bug in the bash shell. Everything's functioning as it should.

  • 1
    Your first statement contradicts what the user is seeing. Printing the value of var in the global scope, after calling innerFunc through outFunc, does not print new value. – Kusalananda Oct 17 '18 at 18:25

You can use a function to force local scope:

sh_local() {
  eval "$(set)" command eval '\"\$@\"'


x() {
  z='new value'
  printf 'function x, z = [%s]\n' "$z"
y() {
  z='initial value'
  printf 'function y before x, z = [%s]\n' "$z"
  sh_local x
  printf 'function y after x, z = [%s]\n' "$z"
printf 'global before y, z = [%s]\n' "$z"
printf 'global after y, z = [%s]\n' "$z"


global before y, z = []
function y before x, z = [initial value]
function x, z = [new value]
function y after x, z = [initial value]
global after y, z = [initial value]


  • iiuc, in this code, x is running in a separate process (because of command), so its logging message is coming from that process, not from the process in which y is running. – Greg Minshall Apr 28 at 2:26
  • @GregMinshall I dont really know, shell code is awful, so I dont use it anymore really – Steven Penny Apr 28 at 2:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.