Control and functions

Write small functions

Extract repeated work into functions that accept arguments and return useful status.

10 minute lesson

~~~

A shell function is a named block of commands. It shares the script environment — same variables, same working directory — so it behaves less like a function in JavaScript or Python and more like a labeled section of your script. Keep it small, use local variables, and return status through command success.

Define one focused helper

require_command() {
  local name=$1
  command -v "$name" >/dev/null 2>&1 || {
    printf 'missing command: %s\n' "$name" >&2
    return 1
  }
}

require_command curl || exit 1

Inside a function, $1, $2, and $# refer to the function’s own arguments, not the script’s. local name=$1 gives the value a readable name that exists only inside the function.

return sets the function’s exit status the way exit sets the script’s. A function without an explicit return reports the status of its last command — the same rule scripts follow.

Verify both outcomes

Call it with an installed and missing command:

require_command curl
printf '%s\n' "$?"
# 0

require_command doesnotexist
# missing command: doesnotexist
printf '%s\n' "$?"
# 1

The function’s output helps humans; its return status helps the caller. Keep the two channels separate: messages to standard error, results through the status.

The failure mode: hidden globals

Forget local and every variable a function touches leaks into the whole script:

count_lines() {
  file=$1            # no local — overwrites any outer $file
  wc -l < "$file"
}

file=config.txt
count_lines access.log
printf '%s\n' "$file"
# access.log — the caller's variable was silently replaced

These bugs are miserable to find because the damage appears far from the function that caused it. Avoid hidden global mutations: pass values in as arguments and mark internal variables local.

When a function needs to hand a value back, print it and let the caller capture it with command substitution:

lines=$(count_lines access.log)

That keeps data flowing through explicit channels — arguments in, output and status out — which is what makes a function safe to reuse.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →