Functions

Write reusable, modular code with Bash functions

Defining Functions

Functions help organize code into reusable blocks. There are two ways to define them:

# Method 1: function keyword
function greet() {
    echo "Hello, World!"
}

# Method 2: without function keyword (POSIX compatible)
greet() {
    echo "Hello, World!"
}

# Call the function
greet

Function Parameters

Functions can accept arguments just like scripts:

#!/bin/bash

# Function with parameters
greet_user() {
    local name=$1
    local age=$2
    echo "Hello, $name! You are $age years old."
}

# Call with arguments
greet_user "Alice" 25
greet_user "Bob" 30

# Access all parameters
print_all() {
    echo "Number of args: $#"
    echo "All args: $@"
    echo "Script name: $0"
    echo "First arg: $1"
}

print_all apple banana cherry
            

Parameter Variables

$1-$9    # First 9 arguments
${10}    # 10th argument and beyond
$#       # Number of arguments
$@       # All arguments (separate words)
$*       # All arguments (single word)
$0       # Script/function name

Return Values

Functions can return values using the return statement:

#!/bin/bash

# Function to calculate sum
calculate() {
    local sum=$(($1 + $2))
    return $sum
}

calculate 5 10
result=$?
echo "Sum: $result"

Local Variables

Use local variables to avoid conflicts with global scope:

BASH
#!/bin/bash

# Global variable
counter=10

increment() {
    local counter=0  # Local to function
    ((counter++))
    echo "Local counter: $counter"
}

increment
echo "Global counter: $counter"  # Still 10

# Without local
increment_global() {
    ((counter++))  # Modifies global
}

increment_global
echo "Global counter: $counter"  # Now 11
⚠️ Important: Always use local for function variables to prevent unexpected side effects!

Default Parameter Values

# Set default values
greet() {
    local name=${1:-"Guest"}
    local greeting=${2:-"Hello"}
    echo "$greeting, $name!"
}

greet                    # Hello, Guest!
greet "Alice"            # Hello, Alice!
greet "Bob" "Welcome"    # Welcome, Bob!

# Check if parameter is provided
process_file() {
    local file=$1
    
    if [ -z "$file" ]; then
        echo "Error: No file specified"
        return 1
    fi
    
    echo "Processing $file..."
}

Recursive Functions

# Factorial calculation
factorial() {
    local n=$1
    
    if [ $n -le 1 ]; then
        echo 1
    else
        local prev=$(factorial $((n - 1)))
        echo $((n * prev))
    fi
}

result=$(factorial 5)
echo "5! = $result"  # 120

# Fibonacci sequence
fibonacci() {
    local n=$1
    
    if [ $n -le 1 ]; then
        echo $n
    else
        local a=$(fibonacci $((n - 1)))
        local b=$(fibonacci $((n - 2)))
        echo $((a + b))
    fi
}

echo "Fibonacci(7) = $(fibonacci 7)"  # 13

Function Best Practices

#!/bin/bash

# 1. Use descriptive names
validate_email() {
    local email=$1
    [[ $email =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]
}

# 2. Document your functions
# Backs up a file to a specified directory
# Args:
#   $1 - Source file path
#   $2 - Backup directory (optional, defaults to ./backups)
# Returns:
#   0 on success, 1 on failure
backup_file() {
    local source=$1
    local dest_dir=${2:-./backups}
    
    # Implementation...
}

# 3. Validate inputs
process_user() {
    local username=$1
    
    if [ $# -ne 1 ]; then
        echo "Usage: process_user " >&2
        return 1
    fi
    
    if [ -z "$username" ]; then
        echo "Error: Username cannot be empty" >&2
        return 1
    fi
    
    # Process user...
}

# 4. Use local variables
calculate_discount() {
    local price=$1
    local discount_percent=$2
    local discount=$((price * discount_percent / 100))
    local final_price=$((price - discount))
    echo $final_price
}

# 5. Proper error handling
safe_delete() {
    local file=$1
    
    if [ ! -f "$file" ]; then
        log_error "File not found: $file"
        return 1
    fi
    
    if ! rm "$file"; then
        log_error "Failed to delete: $file"
        return 1
    fi
    
    log_info "Deleted: $file"
    return 0
}

Advanced Function Examples

Menu System

#!/bin/bash

show_menu() {
    echo "=== Main Menu ==="
    echo "1) List files"
    echo "2) Show disk usage"
    echo "3) Check memory"
    echo "4) Exit"
}

list_files() {
    echo "Files in current directory:"
    ls -lh
}

disk_usage() {
    echo "Disk usage:"
    df -h
}

check_memory() {
    echo "Memory usage:"
    free -h
}

main() {
    while true; do
        show_menu
        read -p "Select option: " choice
        
        case $choice in
            1) list_files ;;
            2) disk_usage ;;
            3) check_memory ;;
            4) echo "Goodbye!"; exit 0 ;;
            *) echo "Invalid option" ;;
        esac
        
        echo
        read -p "Press Enter to continue..."
    done
}

main

Configuration Loader

#!/bin/bash

load_config() {
    local config_file=$1
    
    if [ ! -f "$config_file" ]; then
        echo "Config file not found: $config_file" >&2
        return 1
    fi
    
    # Source config file safely
    while IFS='=' read -r key value; do
        # Skip comments and empty lines
        [[ $key =~ ^#.*$ ]] && continue
        [[ -z $key ]] && continue
        
        # Export variables
        export "$key=$value"
    done < "$config_file"
}

# config.txt:
# DB_HOST=localhost
# DB_PORT=5432
# DB_NAME=myapp

load_config "config.txt"
echo "Connecting to $DB_HOST:$DB_PORT/$DB_NAME"