Shell builtins · bash builtin
local
Declare variables inside a function that disappear when it returns, leaving any global of the same name untouched.
local [OPTION] NAME[=VALUE] ...
Variables in bash are global by default, even when they are first assigned inside a
function. A helper that uses i or result will overwrite the caller's i or result.
local NAME creates a variable that exists only while the function runs; when it returns,
the outer value (or the absence of one) comes back.
bash uses dynamic scoping: a local is visible not only in its own function but in every
function it calls. So a callee can read, and change, its caller's locals. That is different
from most programming languages and occasionally useful, but mostly a reason to give every
function-internal variable a local declaration.
local accepts the same options as declare (-a, -A, -i, -r, -n, ...) and is
only valid inside a function. One trap deserves attention: local x=$(cmd) hides the exit
status of cmd. Declare first, then assign.
Examples
Keep a function's variables to itself
name="global"
without() { name="changed by without"; }
with() { local name="changed by with"; echo "inside with: $name"; }
with; echo "after with: $name"
without; echo "after without: $name"
inside with: changed by with after with: global after without: changed by without
The local in with shadows the global only while the function runs. without
assigns to the global itself, which is the default and usually a bug.
A missing local clobbers the caller's loop variable
helper() { i=99; }
for ((i = 0; i < 3; i++)); do helper; echo "i=$i"; done
i=99
helper uses i for its own purposes, but without local that is the caller's loop
counter. It becomes 99 and the loop ends after one round. local i=99 inside helper
would fix it. (Calling the function through $(...) would hide the bug, because that
runs it in a subshell.)
Locals are visible to called functions
show() { echo "show sees level=$level"; }
outer() { local level=outer; show; }
level=global
outer
show
show sees level=outer show sees level=global
show has no level of its own, so it looks it up at call time and finds outer's
local. Called directly, it sees the global. This is dynamic scoping.
local hides the exit status of command substitution
bad() { local out=$(grep -c CRITICAL logs/app.log); echo "status after local=: $?"; }
good() { local out; out=$(grep -c CRITICAL logs/app.log); echo "status after split: $?"; }
bad; good
status after local=: 0 status after split: 1
local itself succeeded, so $? is 0 even though grep found nothing and returned 1.
Declaring on one line and assigning on the next keeps grep's status, which set -e
and if need to see.
local only works inside a function
local x=1
echo "status $?"
/bin/bash: line 1: local: can only be used in a function status 1
At the top level, bash prints an error and local returns 1. Use a plain assignment, or
wrap the code in a function.
Local arrays and integers
stats() {
local -a nums
local -i sum=0 n
mapfile -t nums < data/numbers.txt
for n in "${nums[@]}"; do sum+=n; done
echo "${#nums[@]} numbers, sum $sum"
}
stats
echo "outside: nums has ${#nums[@]} elements"
10 numbers, sum 324 outside: nums has 0 elements
local takes declare's options: -a for an array, -i for integer arithmetic on
assignment (so sum+=n adds). None of the variables leak out of the function.
Restore shell options when the function returns with local -
literal() { local -; set -f; echo "inside: " *.txt; }
literal
echo "outside: " *.txt
inside: *.txt outside: file with spaces.txt notes.txt todo.txt
local - (bash 4.4 and later) saves the set options and puts them back when the
function returns. Here set -f turns off globbing inside literal only, so *.txt
prints as is there and expands to file names afterward.
Make a variable unset inside one function
export LANG=C.UTF-8
plain() { local LANG; unset LANG; echo "inside: LANG=[${LANG-unset}]"; }
plain
echo "outside: LANG=$LANG"
inside: LANG=[unset] outside: LANG=C.UTF-8
local LANG makes a local copy, and unset then removes only that copy. The caller's
value returns when the function ends.
Common pitfalls
Forgetting
localfor loop counters and temporary variables: the function silently overwrites the caller's variables of the same name.local var=$(cmd)always returns 0. Split it intolocal varandvar=$(cmd)when the status matters.Assuming locals are private: functions called from yours can read and modify them (dynamic scoping). Use distinctive names in library code.
A
localarray declared withlocal arr=(...)is fine, butlocal -Ais required for associative arrays, just as withdeclare.
GNU vs BSD and macOS
The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).
localis not in POSIX, but dash, busybox sh, ksh93 (astypeset) and zsh all have it. dash'slocaldoes not takedeclare-style options such as-aor-i.
Options worth knowing
A selection, described in our own words. For the full list, see bash(1), SHELL BUILTIN COMMANDS.
| Option | What it does |
|---|---|
-a | Make a local indexed array. |
-A | Make a local associative array. |
-i | Give the local the integer attribute. |
-r | Make the local read-only for the rest of the function. |
-n | Make the local a reference to the variable named by its value. |
-x | Export the local to commands the function runs. |
- | Save the shell options and restore them when the function returns (bash 4.4+). |
Related
declareCreate variables with attributes (array, associative array, integer, read-only, exported), or show how variables are defined.unsetRemove shell variables, array elements or functions, so they no longer exist rather than being empty.exportMark shell variables (or bash functions) to be passed to every program the shell starts.- FunctionsDefining and calling bash functions, arguments, local variables, return status versus output, namerefs, and sourcing a function library.
- VariablesAssigning and reading shell variables, the no-spaces rule, braces, unset vs empty, scope, readonly, and the special variables bash sets for you.
- Exit codesWhat exit statuses mean, how $? works, the reserved values 126, 127 and 128+N, PIPESTATUS, and how to choose codes for your own scripts.