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.
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[*]}"
}
processScope 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_valueBash 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
- Declare all function-internal variables with
local - Name function parameters descriptively:
local filename="$1"vs just$1 - Use
local -rfor constants inside functions - Use
local -ifor variables that should only hold integers
Without `local`, where is a variable created inside a function visible?
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