Skip to content
Functions
step 1/5

Reading — step 1 of 5

Learn

~2 min readFunctions 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

A square function plus read/call/print plumbing:

square() {
    local n=$1
    echo "$(( n * n ))"
}

read x
square "$x"

Note the last line: since square prints its answer, calling it IS printing — no capture needed when the output goes straight to the grader (capture with $( ) when you need the value for further work). The graded habits: local on the function's variable, quoted "$x" at the call, logic in the function and I/O outside — the same separation as every other track, expressed 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…