Step 1 of 5 · Reading · ~3 min
Learn
Functions and Pipes
Functions
Bash functions are miniature scripts living inside your script: they take arguments the same way, return exit statuses the same way, and "return" data the way commands do — by printing it. Embrace the command-ness and they're simple; fight it and they're baffling.
Defining and calling
greet() {
echo "Hello, $1"
}
greet "Ada" # call = invoke like a command. No parentheses!
greet "Grace"
Arguments arrive as positional parameters: $1, $2, … plus $# (how many) and "$@" (all of them, properly quoted — the only correct way to forward arguments). Nothing is declared in the parentheses — they're purely syntax marking "this is a function."
Getting data OUT: print it
Here's the mental flip. return in bash sets the exit status (0–255, success/failure) — it cannot return data. Data leaves a function on stdout, and the caller captures it with command substitution:
square() {
echo "$(( $1 * $1 ))"
}
result=$(square 7) # capture the function's output
echo "$result" # 49
$( … ) runs a command (or function — same thing!) and substitutes its output. This is exactly how you capture $(date) or $(wc -l < file); functions earn no special treatment because they are commands. Corollary that bites everyone once: anything a function prints becomes part of its "return value" — a stray debug echo inside corrupts the captured result. (Debug prints belong on stderr: echo "debug" >&2 — pipes lesson formalizes this.)
return still matters for its real job — signaling success/failure:
is_even() {
(( $1 % 2 == 0 )) # arithmetic test's status becomes the function's status
}
if is_even 4; then echo "even"; fi # functions as conditions — very bash
local: the keyword that keeps you sane
Variables in bash are global by default — even inside functions:
count_things() {
n=$(( $1 + 1 )) # silently overwrites any outer $n !
}
count_things 5
echo "$n" # 6 — your function leaked
The fix is local n=$(( $1 + 1 )) — scoped to the function, gone at return. The professional rule has no exceptions worth learning: every variable inside a function gets local. It's the cheapest bug insurance in the language; unscoped helper variables colliding across functions produce the kind of spooky, order-dependent bugs that take evenings.
Your exercise: Square It
The starter has the plumbing -- a function stub, the read, and the call. Your job is the body.
Remember where the answer has to go. return cannot hand back a number; data leaves a bash function on stdout. So the body prints, and because the call site is the last line of the script, that print IS the program's output -- no capture needed. (Capture with $( ) when you need the value for more work.)
The graded habits: local on the function's own variable, quoted "$x" at the call site, and the arithmetic done inside an arithmetic context -- a body that echoes "(($1*$1))" in quotes prints those characters, which is the single most common wrong answer in this exercise's history. Logic inside the function, I/O outside it: the same separation as every other track, in bash's native idiom of small programs calling smaller ones.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…