Package reusable logic into Bash functions: define them, pass arguments, return status with exit codes, capture output, keep variables local, and call a function from itself.
Why: a function groups commands under a name so you can reuse them and give your script structure. Define it before you call it. Inside, the arguments arrive as $1, $2, … — exactly like a script's positional parameters.
#!/usr/bin/env bash
greet() {
echo "Hello, $1"
}
greet "Ada" # Hello, Ada
greet "Grace" # Hello, GraceWhy: without local, a variable set in a function is global and can silently overwrite one elsewhere. Declaring local makes it private to the function — the habit that keeps larger scripts from tripping over themselves.
#!/usr/bin/env bash
slugify() {
local text="$1"
local result="${text// /-}" # spaces -> dashes
echo "${result,,}" # lower-case
}
slugify "My First Post" # my-first-postWhy: return sets a function's exit code (0 = success, non-zero = failure) so callers can test it with if — just like any command. A function is "true" or "false" through this code, NOT through what it echoes.
#!/usr/bin/env bash
is_installed() {
command -v "$1" > /dev/null # 0 if found, non-zero if not
}
if is_installed git; then
echo "git is available"
else
echo "please install git"
fiWhy: to get a value out of a function, echo it and capture that with command substitution. Reserve return for the success/failure code; use stdout for the actual result.
#!/usr/bin/env bash
timestamp() {
date +%H:%M:%S
}
now=$(timestamp) # capture what it echoed
echo "started at $now"Why: a function may call itself, with a base case that stops the recursion. It is occasionally the clearest way to walk a tree or compute a factorial — though a loop is usually simpler in shell.
#!/usr/bin/env bash
countdown() {
local n="$1"
[[ "$n" -le 0 ]] && { echo "liftoff"; return; } # base case
echo "$n"
countdown "$(( n - 1 ))" # recurse
}
countdown 3 # 3, 2, 1, liftoff