<aside> 🧱

Module 8 — Functions and Script Structure. Real scripts grow. Functions keep a 300-line deploy script readable, testable, and safe to change.

🧠 concept → 🧩 analogy → 🧪 exercise → ✅ expected result (hidden) → 🎯 interview questions (hidden)

</aside>

<aside> 📋

Before you start. You need Modules 1–7 — especially positional parameters (Module 7: functions reuse that exact machinery), exit codes (Module 3: functions have verdicts too), and source vs execute (Module 1 C1: it returns here with a twist). Tools: nothing new. Keep ~/bash-course.

</aside>

Part A — Defining and calling

A1. A function is a named block, defined before it is called

<aside> 📖

Official docs: Bash manual — Shell Functions

</aside>

A function gives a block of commands a name; saying the name later runs the block. The standard form:

log() {
    echo "[$(date +%H:%M:%S)] $*"
}
log "deploy starting"

The parentheses are empty punctuation marking "this is a function definition" — arguments do not go there (A2 shows where they go). The body sits in { }. An alternative spelling function log { … } exists in bash; the name() form is the portable convention, and mixing both in one file is a style smell.

One rule follows from bash's nature as a top-to-bottom reader (Module 1): a function must be defined earlier in the file than its first call. There is no hoisting, no lookahead — a call above the definition is just an unknown command (exit 127, Module 3's old friend). Hence the universal layout: functions at the top, action at the bottom — C1 makes that a formal template.

<aside> 🧩

Real-world analogy — the named play. A sports team drills a set-piece until it has a name — "run Falcon!" — and one shouted word triggers the whole rehearsed sequence. Functions are named plays: define once, invoke by name, and the game plan (your main script) reads as strategy instead of choreography.

Where the analogy stops working. A team remembers plays from last season. Bash remembers nothing across runs: definitions live only in the shell that read them, die with it (Module 2's jars), and must appear in every script that uses them — which is why shared functions get a file of their own that scripts source (C2), the closest bash has to a playbook binder.

</aside>

🧪 Exercise 8.1 — name a play

cd ~/bash-course
nano fn1.sh     # six lines:
#!/bin/bash
log() {
    echo "[$(date +%H:%M:%S)] $*"
}
log "deploy starting"
log "deploy finished"
chmod +x fn1.sh
./fn1.sh

A2. Function arguments: $1 again, wearing a different hat

<aside> 📖

Official docs: Bash manual — Shell Functions

</aside>

Arguments go after the function's name at the call, exactly like arguments to a script — and inside the function, they arrive as $1, $2, $#, "$@": the same positional machinery, temporarily re-pointed. For the duration of the call, the function's arguments shadow the script's; when it returns, the script's own $1 is back, untouched. Everything Module 7 taught applies wholesale — quoting, $# guards, shift, even getopts (with the OPTIND reset from Module 7's E2).

🧪 Exercise 8.2 — arguments in, arguments restored