Module 9 · Shell scripting basics
Functions
Name a block of commands, call it like a command with its own arguments, keep its variables local and reuse it from a library file.
What you will learn
- Define functions with the portable `name() { … }` syntax and call them.
- Pass arguments and read them as `$1`, `$2`, `$#` inside the function.
- Use `local`, `return` and `echo` to keep state tidy and get results out.
When the same four lines appear twice in a script, make them a function: a named block you can call like any command. Functions must be defined before they are called, so they go near the top of the script, and the "main" code goes at the bottom. The portable syntax is name() { commands; }; the function keyword you may see in bash tutorials does not exist in POSIX sh and fails here.
~% cat lab/l87/area.sh
#!/bin/sh
# Print a line of dashes, then the title
banner() {
echo "----------------"
echo "$1"
echo "----------------"
}
# Area of a rectangle: width height
area() {
local w="$1" h="$2"
echo $(( w * h ))
}
banner "Areas"
area 3 4
area 5 6
echo "Room: $(area 10 12) m2"
~% lab/l87/area.sh
----------------
Areas
----------------
12
30
Room: 120 m2
Inside a function, $1, $2 and $# refer to the function's arguments, not the script's; the script's own arguments are hidden while the function runs and come back afterwards. Space-separated words after the name become the arguments, exactly as with external commands, and the same quoting rules apply: banner "Daily report" is one argument, banner Daily report is two.
Getting results out
A function has two channels back to the caller. Its output is whatever it prints, which you capture with $(area 10 12) like any command. Its exit status is set by return N (0 to 255) or, if there is no return, by the last command it ran. Use return for yes/no answers so the function works with if and &&, and use echo for values. Do not try to return a number you want to use: return 300 wraps around and return cannot carry text.
~% is_even() { [ $(( $1 % 2 )) -eq 0 ]; }
~% is_even 4 && echo "4 is even"
4 is even
~% if is_even 7; then echo even; else echo odd; fi
odd
~% need() { command -v "$1" >/dev/null || { echo "missing: $1" >&2; return 1; }; }
~% need tar && need gzip && echo "all tools present"
all tools present
local variables and libraries
Variables are global by default: a tmp=… inside a function overwrites any tmp the caller had. local w h declares names that exist only until the function returns; BusyBox ash supports it, and so does every shell you are likely to meet, although strictly speaking it is not in POSIX. Declare every helper variable local and you will never debug a mysteriously changed value again.
Functions you use in several scripts belong in a library file with no shebang and no main code, only definitions. Each script loads it with . /root/lib/utils.sh and then calls the functions as if they were its own. This is exactly how the dot command from lesson 82 earns its keep.
Commands in this lesson
| Command | What it does |
|---|---|
name() { …; } | Define a function (POSIX syntax). |
name arg1 arg2 | Call it; args become $1, $2. |
local x y | Variables private to the function. |
return N | Exit status of the function (0–255). |
result=$(name args) | Capture what the function prints. |
. /path/lib.sh | Load a function library. |
Quiz
Which definition is valid in POSIX sh (and in this VM)?
- `function greet { echo hi; }`
- `greet() { echo hi; }`
- `def greet: echo hi`
Inside a function, what is `$1`?
- The script's first argument
- The function's first argument
- The function's name
How do you get a text value out of a function?
- `return "$text"`
- `echo "$text"` and capture with `$( )`
- `exit "$text"`
What does `local` achieve?
- The variable disappears when the function returns
- The variable becomes read-only
- The variable is exported to child processes
Practice
Write an executable script `/root/lab/l87/shout.sh` that defines a function `shout` which prints its arguments in upper case (use `tr`), then calls `shout hello world`. Running the script must print `HELLO WORLD`.
Write an executable script `/root/lab/l87/area.sh` with a function `area` that prints the product of its two arguments, and call it twice: `area 3 4` and `area 5 6`. The output must be `12` and `30` on two lines.
The library `/root/lab/l87/utils.sh` defines a function `banner`. Write an executable script `/root/lab/l87/use.sh` that loads it with `.` (absolute path) and calls `banner Done`.
Open this lesson in the app to do the tasks in a real Linux machine and have them checked.