Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Loops and Functions  ›  Capstone Project

local, Scope and the Finished Script

Function Scopes 15 minVariables in Bash are global by default, so a function should declare its working variables with local, which keeps them visible only inside that function and the functions it calls.

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; local keeps 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=value creates 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 a has local v=inner and calls b, then b sees inner.
  • 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     # 24

The 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 failed
./backup.sh weekly ~/notes "My Photos"Step 1 of 4
  1. Check and shift

    $# is 3, so the guard passes. label is weekly; after shift, "$@" holds the two folders.

Drills

Answer fast — trust the rule.

quick drill

What does x=1; f() { x=2; }; f; echo "$x" print?

quick drill

What does x=1; g() { local x=2; }; g; echo "$x" print?

quick drill

After a() { local v=inner; b; } and b() { echo "$v"; }, what does v=outer; a print?

quick drill

Using the fact function above, what does fact 4 print?

Remember

  • Variables are global by default; local keeps them inside the function.
  • First line of every function: local for everything it creates.
  • Scope is dynamic: called functions see their caller's locals.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainExplain backup.sh to a teammate

Using the finished backup.sh from this lesson, explain to a teammate what happens when they run ./backup.sh weekly ~/notes "My Photos": the checks, the loop, the functions, and the exit status. Point out one place where a missing local or missing quotes would cause a real bug.