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.