local, Scope and the Finished Script
backup.sh has a count of archived folders. You add a helper function that uses a variable called count for something else, and suddenly the final report says "Archived 0 folders". Nothing warned you. In Bash, a function's variables leak by default.
The rule
Variables are global by default;
localkeeps them inside the function.
x=1
change() { x=2; }
protect() { local x=2; }
protect; echo "$x" # 1 the local x vanished when protect ended
change; echo "$x" # 2 change overwrote the global x- Any variable assigned in a function, without
local, is global: it changes or creates the script's variable. local name=valuecreates a variable that exists only while that function runs.- Bash scope is dynamic: a function called from another function can see the caller's locals. If
ahaslocal v=innerand callsb, thenbseesinner. - Local variables make recursion safe, because every call gets its own copy:
fact() {
local n=$1
if (( n <= 1 )); then echo 1
else echo $(( n * $(fact $(( n - 1 ))) )); fi
}
fact 4 # 24The shortcut
Every variable a function creates gets local, unless the function's job is to set a global. Make it the first line of every function.
Here is the finished backup.sh, using every section of this course:
#!/usr/bin/env bash
# backup.sh: pack each folder into a dated archive
# usage: backup.sh LABEL FOLDER...
dest="$HOME/backups"
stamp="$(date +%F)"
failed=0
log() { printf '[%s] %s\n' "$(date +%T)" "$*" >&2; }
archive_one() {
local dir="${1%/}"
local name="${dir##*/}"
local archive="$dest/$label-${name// /_}-$stamp.tar.gz"
tar -czf "$archive" "$dir" && log "Saved $archive"
}
if (( $# < 2 )); then
log "usage: $0 LABEL FOLDER..."
exit 2
fi
label="$1"; shift
mkdir -p "$dest" || exit 1
for dir in "$@"; do
if [[ ! -d "$dir" ]]; then
log "Skipping $dir: not a folder"
(( failed++ )); continue
fi
archive_one "$dir" || (( failed++ ))
done
(( failed == 0 )) # the script's exit status: 0 only if nothing failedCheck and shift
$#is 3, so the guard passes.labelisweekly; aftershift,"$@"holds the two folders.
Drills
Answer fast — trust the rule.
What does x=1; f() { x=2; }; f; echo "$x" print?
What does x=1; g() { local x=2; }; g; echo "$x" print?
After a() { local v=inner; b; } and b() { echo "$v"; }, what does v=outer; a print?
Using the fact function above, what does fact 4 print?
Remember
- Variables are global by default;
localkeeps them inside the function. - First line of every function:
localfor everything it creates. - Scope is dynamic: called functions see their caller's locals.
