<aside> 📥

Module 7 — Script Arguments and User Input. A script you can only edit is a note; a script that takes arguments is a tool. How deploy.sh staging --dry-run actually works.

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

</aside>

<aside> 📋

Before you start. You need Modules 1–6. Load-bearing: quoting (Module 2 — the "$@" story is a quoting story), exit codes and the usage-error convention (Module 3), >&2 (Module 4), case and [ ] (Module 5), while and arithmetic (Module 6 — getopts is a while-case machine). Module 5's route.sh already used $1 on credit; this module pays that debt. Tools: nothing new.

</aside>

Part A — Positional parameters: what the caller typed

A1. $1, $2, … $0, and $#

<aside> 📖

Official docs: Bash manual — Special Parameters

</aside>

When someone runs ./greet.sh staging blue, bash takes the words after the script's name and files them into numbered variables before line one executes: $1 is staging, $2 is blue — the positional parameters. Three relatives complete the set: $0 holds the name the script was invoked by (./greet.sh here — useful in usage messages); $# holds the count of arguments (2 here); and positions past nine need braces — ${10} — because $10 reads as $1 followed by a literal 0 (Module 2's greedy-name rule biting in a new place).

These are variables with the usual rules — which means the usual reflex: "$1" in quotes, everywhere — an argument can contain spaces (a filename, a message) and unquoted it word-splits exactly like any other expansion. And an argument never supplied expands to nothing, silently (Module 2 A1's silence — now arriving from the outside world, where you control it even less).

<aside> 🧩

Real-world analogy — the job ticket's numbered fields. A print-shop job ticket has numbered blanks: field 1 — paper size, field 2 — copies. The customer fills the blanks; the machine operator (your script) reads them by number and never asks who wrote them. The shop's name printed on the ticket header is $0; the "fields filled: N" tally is $#.

Where the analogy stops working. A paper form shows its empty fields — you can see blank where "copies" should be. A script cannot see absence: $2 unset and $2 never-provided look identical (empty), and the machine runs anyway with a blank where a value belonged. That is why Part A2's counting guard exists — the tally $# is the only witness that fields were left unfilled.

</aside>

🧪 Exercise 7.1 — the numbered blanks

The second run is incomplete on purpose.

cd ~/bash-course
nano greet.sh     # four lines:
#!/bin/bash
echo "script name: $0"
echo "first arg:   $1"
echo "second arg:  $2"
echo "arg count:   $#"
chmod +x greet.sh
./greet.sh staging blue
./greet.sh              # ← no arguments at all

A2. The counting guard: fail fast, fail helpfully

Module 3 established the convention (usage error → message on stderr, exit 2); Module 5 built guards; here they fuse into the first line of virtually every production script:

[ "$#" -ge 1 ] || { echo "usage: $0 command [targets...]" >&2; exit 2; }

Read it with your accumulated toolkit: $# counted, [ -ge ] compared numerically (Module 5 B2), || fired on failure (Module 3), the message named the script via $0 and left on channel 2 (Module 4), and exit 2 told the caller which kind of failure. The { …; } braces group the two commands so || owns both — a small new piece of syntax doing exactly what it looks like. A script guarded this way fails in a millisecond with instructions, instead of failing halfway through real work with a mystery.

Interview questions — Part A