Functions & Modularity

8. Functions & Modularity#

In this chapter, you’ll discover how to write reusable, maintainable code through bash functions. Functions are the building blocks of modular programming—they encapsulate logic, reduce code duplication, and make scripts easier to test and debug.

By mastering functions, you’ll transform scripts from flat sequences of commands into well-structured programs that scale with complexity.

What You'll Learn

By the end of this chapter, you will be able to:

✓ Define and invoke functions in bash scripts ✓ Understand local vs. global variable scope ✓ Return values and work with exit codes (\(?) ✓ Create reusable function libraries ✓ Handle function parameters and arguments (\)@, \(1, \)2, etc.) ✓ Implement recursive functions ✓ Debug function behavior ✓ Organize large scripts into modular pieces

Chapter Map

Section

Topic

Key Concepts

0802

Defining & Invoking Functions

Function syntax, parameters, calling

0803

Local vs. Global Scope

Variable visibility, scope rules

0804

Return Values & Status Codes

return statement, $?, exit codes

0805

Reusable Function Libraries

Sourcing files, shared functions

0806

Lab: Modular Scripting

Build libraries and modular programs

Why This Matters

Functions enable professional, scalable scripting:

  • Code reuse: Write once, use many times

  • Testing: Test functions independently

  • Maintenance: Fix bugs in one place

  • Readability: Well-named functions make code self-documenting

  • Scalability: Turn 1000-line scripts into organized modules

  • Collaboration: Team members can work on different functions

Real-world impact: A 100-line script with functions is easier to maintain than a 50-line script without them. Functions are the bridge from hacks to professional code.

Prerequisites

You should already understand:

  • Variables and control flow (Chapters 5-7)

  • How scripts are executed

  • Basic command-line concepts

  • Operators and conditionals

If loops or conditions feel unclear, review Chapter 7 first.

Real-World Scenario

Imagine building a system administration toolkit. Without functions, you’d repeat error handling everywhere:

#!/bin/bash

# Without functions - repetitive
echo "Starting backup..."
if [ -d "/data" ]; then
    tar -czf backup.tar.gz /data
    if [ $? -eq 0 ]; then
        echo "✓ Backup succeeded"
    else
        echo "✗ Backup failed"
        exit 1
    fi
else
    echo "✗ /data directory not found"
    exit 1
fi

# With functions - clean and maintainable
backup_directory() {
    local src="$1"
    local dest="$2"
    
    if [ ! -d "$src" ]; then
        echo "✗ Source directory not found: $src"
        return 1
    fi
    
    tar -czf "$dest" "$src" || return 1
    echo "✓ Backup created: $dest"
}

# Now you can use it repeatedly
backup_directory "/data" "data-backup.tar.gz"
backup_directory "/etc" "etc-backup.tar.gz"
backup_directory "/home" "home-backup.tar.gz"

Functions eliminate repetition and make your code cleaner.

Sample Script: System Utility Library

A reusable library of system administration functions:

#!/bin/bash
# lib/sysadmin.sh - Reusable system administration functions

# Color codes for output
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly NC='\033[0m'

# Log messages with levels
log_info() {
    echo -e "${GREEN}[INFO]${NC} $*"
}

log_warn() {
    echo -e "${YELLOW}[WARN]${NC} $*" >&2
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $*" >&2
}

# Check if command exists
require_command() {
    local cmd="$1"
    if ! command -v "$cmd" &>/dev/null; then
        log_error "Required command not found: $cmd"
        return 1
    fi
}

# Check if running as root
require_root() {
    if [ $EUID -ne 0 ]; then
        log_error "This function requires root privileges"
        return 1
    fi
}

# Check if file exists and is readable
require_file() {
    local file="$1"
    if [ ! -r "$file" ]; then
        log_error "File not found or not readable: $file"
        return 1
    fi
}

# Restart a service safely
restart_service() {
    local service="$1"
    
    require_root || return 1
    
    log_info "Restarting $service..."
    systemctl restart "$service"
    
    if [ $? -eq 0 ]; then
        log_info "$service restarted successfully"
        return 0
    else
        log_error "Failed to restart $service"
        return 1
    fi
}

# Get disk usage percentage
get_disk_usage() {
    local path="${1:-.}"
    df "$path" | tail -1 | awk '{print int($5)}'
}

# Check system resources
check_resources() {
    local cpu_threshold="${1:-80}"
    local mem_threshold="${2:-80}"
    local disk_threshold="${3:-90}"
    
    local cpu=$(top -bn1 | grep "Cpu(s)" | awk '{print int($2)}')
    local mem=$(free | grep Mem | awk '{print int($3/$2 * 100)}')
    local disk=$(get_disk_usage "/")
    
    log_info "System Resources:"
    [ $cpu -gt $cpu_threshold ] && \
        log_warn "CPU: ${cpu}% (threshold: ${cpu_threshold}%)" || \
        log_info "CPU: ${cpu}%"
    
    [ $mem -gt $mem_threshold ] && \
        log_warn "Memory: ${mem}% (threshold: ${mem_threshold}%)" || \
        log_info "Memory: ${mem}%"
    
    [ $disk -gt $disk_threshold ] && \
        log_warn "Disk: ${disk}% (threshold: ${disk_threshold}%)" || \
        log_info "Disk: ${disk}%"
}

# Backup configuration
backup_config() {
    local config_file="$1"
    local backup_dir="${2:-.}"
    
    require_file "$config_file" || return 1
    
    local backup_file="$backup_dir/$(basename $config_file).bak.$(date +%s)"
    cp "$config_file" "$backup_file"
    
    if [ $? -eq 0 ]; then
        log_info "Config backed up to: $backup_file"
        return 0
    else
        log_error "Failed to backup $config_file"
        return 1
    fi
}

# Example usage
if [ "${BASH_SOURCE[0]}" == "${0}" ]; then
    # If sourced directly, demonstrate the functions
    log_info "System Administration Library Loaded"
    check_resources 75 85 90
fi

What this library demonstrates:

  • Function definition and parameters

  • Return values and error handling

  • Local vs. global variables

  • Reusable utility functions

  • Professional error handling

  • Practical system administration tasks

Progression

This chapter marks the transition to professional programming:

Level

Chapters

Focus

Beginner

1-5

Fundamentals

Intermediate

6-7

Decision logic

Professional

8-15

Functions, modularity, automation

After mastering functions, you can write enterprise-quality Bash code.