Skip to content

Functions

Functions are what separate a 50-line script from a 500-line one — reuse logic, reduce repetition, and make code testable.

Learning Objectives

  • Define and call functions in bash
  • Pass arguments to functions and return values
  • Use local to scope variables inside functions
  • Source a library of functions from another file

Defining and Calling Functions

# Two equivalent syntaxes
greet() {
    echo "Hello, $1"
}

function greet {
    echo "Hello, $1"
}

# Call it
greet "Nikhil"
Hello, Nikhil

Prefer the name() {} syntax

The function name {} syntax is bash-specific. The name() {} syntax works in POSIX sh and bash. Use name() for portability.


Arguments

Functions receive arguments the same way scripts do — as $1, $2, etc. Inside the function, $@ is the function's arguments, not the script's:

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

sum=$(add 5 3)
echo "Sum: $sum"
Sum: 8


Local Variables

Without local, variables set inside a function pollute the global scope:

bad_function() {
    x=10        # global — visible outside the function
}

good_function() {
    local x=10  # local — invisible outside the function
}

Always use local for function variables

Forgetting local means any variable name collision between your function and the calling code will cause subtle bugs. Make local the default.


Return Values

return sets the exit code (0-255). To return a string, print it and capture with $():

is_even() {
    local n="$1"
    if (( n % 2 == 0 )); then
        return 0   # true/success
    else
        return 1   # false/failure
    fi
}

get_extension() {
    local filename="$1"
    echo "${filename##*.}"
}

# Usage
if is_even 42; then
    echo "42 is even"
fi

ext=$(get_extension "report.pdf")
echo "Extension: $ext"

Sourcing Function Libraries

Define reusable functions in a separate file and source them:

# ~/scripts/lib/utils.sh
log() {
    echo "[$(date +%H:%M:%S)] $*"
}

die() {
    echo "ERROR: $*" >&2
    exit 1
}

require_file() {
    [[ -f "$1" ]] || die "Required file not found: $1"
}
# In your script
source ~/scripts/lib/utils.sh
# or: . ~/scripts/lib/utils.sh

log "Starting backup"
require_file "/etc/backup.conf"

Common Mistakes

Functions must be defined before they are called

Bash reads scripts top to bottom. If you call a function before defining it, you get command not found. Define or source all functions at the top of the script.

return only works in functions

Using return outside a function exits the current source operation, not the script. Use exit to exit the script.


Practice Exercises

Warm-Up (run and observe)

  1. Write a function say_hello that takes a name and prints "Hello, <name>!". Call it three times with different names.
  2. Write a function is_directory that returns 0 if its argument is a directory and 1 otherwise. Test it with if is_directory "/etc".
  3. What happens if you define a function with the same name as a built-in command like echo? How do you call the real echo after that?

Main (write a short script)

Create ~/scripts/lib/utils.sh with a small utility library, then write ~/scripts/backup_simple.sh that sources it:

# ~/scripts/lib/utils.sh
log()     { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
die()     { echo "ERROR: $*" >&2; exit 1; }
info()    { echo "INFO: $*"; }
success() { echo "OK: $*"; }
#!/usr/bin/env bash
set -euo pipefail
source "$(dirname "$0")/lib/utils.sh"

SOURCE="${1:?Usage: $0 <source> <dest>}"
DEST="${2:?Usage: $0 <source> <dest>}"

[[ -d "$SOURCE" ]] || die "Source does not exist: $SOURCE"
log "Starting backup: $SOURCE -> $DEST"
cp -r "$SOURCE" "$DEST"
success "Backup complete"

Stretch

  1. Write a function retry that takes a command and a max attempt count, runs the command, and retries up to N times on failure.
  2. Research how to list all functions defined in your current shell session. What command shows them?
  3. What is the difference between sourcing a file with . vs source? Is there any practical difference in bash?

Interview Questions

  1. Why should you use local for variables inside functions?
Show answer

Without local, variables are global — they can accidentally overwrite variables in the calling scope or leak values to subsequent code. local scopes the variable to the function, making functions self-contained and safe to reuse.

  1. How do you return a string value from a bash function?
Show answer

Print the value with echo (or printf) inside the function, then capture it with command substitution: result=$(my_function arg). return can only pass an integer exit code (0-255), not a string.

  1. What does source file.sh do, and how is it different from bash file.sh?
Show answer

source file.sh (or . file.sh) runs the file in the current shell process — functions and variables defined in it become available in the current session. bash file.sh runs the file in a new child process — nothing defined in it persists after it exits.


day05-part2-find-locate | day01-part2-script-structure