Functions in Bash Scripts

Organise scripts into reusable functions, pass arguments to them and understand the difference between exit codes and printed output.

~6 min Beginner

Why functions?

Suppose your script prints a banner in five places. Copying the same lines five times is messy, and fixing a mistake means fixing it five times. A function is a named block of commands. You write it once and run it by name whenever you need it.

Defining and calling

greet() {
    echo "Hello from a function"
}

greet
greet

The definition has a name, () and a body in { }. Writing the name greet calls the function, which runs its body. This script prints the message twice.

Bash reads a script from top to bottom, so you must define a function before you call it.

Passing arguments

Functions receive arguments in the same way scripts do, as $1, $2 and so on.

greet() {
    echo "Hello, $1!"
}
greet "Alex"
greet "Sam"

This prints Hello, Alex! and Hello, Sam!. Inside a function, $1 is the first argument given to the function, not to the script. $# is the number of arguments and $@ is all of them.

Local variables

By default a variable set inside a function is global, which means it stays visible and can overwrite variables elsewhere in the script. Use local to keep a variable inside the function.

add() {
    local total=$(( $1 + $2 ))
    echo "$total"
}

Make local a habit. It prevents confusing bugs in longer scripts.

Giving results back

A function can give something back in two different ways. They are not the same thing.

1. The exit code

return ends the function with a number from 0 to 255. By convention 0 means success and any other number means failure. if reads this value.

is_even() {
    if [ $(( $1 % 2 )) -eq 0 ]; then
        return 0
    else
        return 1
    fi
}

if is_even 10; then
    echo "10 is even"
fi

% gives the remainder after division, so 10 % 2 is 0.

2. Printed output

To give back real data, such as a number or a sentence, print it with echo and capture it with command substitution:

square() {
    echo $(( $1 * $1 ))
}
result=$(square 7)
echo "7 squared is $result"
Do not use return for answers. It only handles 0 to 255 and is meant for success or failure. Use echo for data.

A complete example

#!/bin/bash

log() {
    echo "[$(date +%H:%M:%S)] $1"
}

check_disk() {
    local usage
    usage=$(df / | awk 'NR==2 {print $5}' | tr -d '%')
    if [ "$usage" -gt 80 ]; then
        log "WARNING: disk is ${usage}% full"
        return 1
    fi
    log "Disk is fine (${usage}%)"
    return 0
}

log "Starting checks"
check_disk
log "Done"

df / shows disk usage, awk picks the percentage column and tr -d '%' removes the % sign so the value can be compared as a number. The main part of the script is a short list of steps: log, check the disk, log. That is the benefit of functions: a short, readable script with the details hidden in named blocks.

Sign up to track your progress Mark lessons as read and pick up where you left off Sign up free to unlock this practice Your first practice lesson is free to try. Create an account to unlock the rest and save your progress