Sagar.BlogArticle
All posts
All posts
Bash

Local Variables and Scope in Bash Functions

Bash variables are global by default — even inside functions. The `local` keyword restricts a variable to its function, preventing subtle bugs in larger scripts.

January 18, 20265 min read
BashFunctionsScopeScripting

Unlike most languages, Bash variables are global by default — even those created inside a function. This means a function can accidentally overwrite a variable in the calling scope. The local keyword fixes this.

The Global Default — A Dangerous Example

count=10    # global variable

function reset_count {
    count=0    # MODIFIES THE GLOBAL — no warning!
}

echo "Before: $count"    # 10
reset_count
echo "After:  $count"    # 0  ← surprise!

The local Keyword

count=10    # global

function reset_count {
    local count=0    # local — doesn't touch the global
    echo "Inside function: $count"   # 0
}

echo "Before: $count"    # 10
reset_count              # Inside function: 0
echo "After:  $count"    # 10  ← unchanged!

Always declare function variables as local

Make it a habit: every variable you create inside a function should be local. The only exceptions are when you intentionally want to modify a global (in which case, name it clearly and document it).

local with Options

function process {
    local name="Alice"         # local string
    local -i count=0           # local integer (-i enforces integer type)
    local -a items=()          # local indexed array
    local -A config=()         # local associative array
    local -r VERSION="1.0"     # local readonly

    items+=("one" "two")
    (( count++ ))
    echo "$name processed $count items: ${items[*]}"
}

process

Scope and Nested Functions

function outer {
    local x="outer_value"

    function inner {
        # inner CAN see local variables from outer — Bash uses dynamic scoping
        echo "inner sees x: $x"
        local x="inner_value"    # shadow outer's x
        echo "inner's x: $x"
    }

    inner
    echo "outer's x after inner: $x"    # unchanged — inner's local didn't affect us
}

outer
# inner sees x: outer_value
# inner's x: inner_value
# outer's x after inner: outer_value

Bash uses dynamic scoping

Unlike most languages (lexical scoping), Bash functions can see local variables from their callers. This works but can be surprising. Rely on it sparingly — it's better to pass values explicitly as arguments.

Best Practices Summary

Function variable conventions

  1. Declare all function-internal variables with local
  2. Name function parameters descriptively: local filename="$1" vs just $1
  3. Use local -r for constants inside functions
  4. Use local -i for variables that should only hold integers
Quick Check

Without `local`, where is a variable created inside a function visible?

Exercise

Fix this buggy script. The function double multiplies its argument by 2, but it's corrupting the result variable in main:

function double {
    result=$(( $1 * 2 ))
    echo "$result"
}

function main {
    result=100
    echo "Before: $result"
    val=$(double 5)
    echo "double(5) = $val"
    echo "After: $result"    # ← should still be 100
}
main