DevOps Journey
Bash

Functions

Bash functions and modular scripts

Functions

Functions are reusable blocks of code that help organize and modularize scripts.

Function Basics

# Define a function
my_function() {
    echo "This is a function"
}

# Alternative syntax
function my_function {
    echo "This is a function"
}

# Call the function
my_function

Function Parameters

# Function with parameters
greet() {
    local name=$1 # first parameter
    local age=$2 # second parameter
    echo "Hello $name, you are $age years old"
}

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

# Access all parameters
print_all() {
    echo "All arguments: $@"
    echo "Argument count: $#"

    for arg in "$@"; do
        echo "- $arg"
    done
}

print_all one two three

Return Values

# Exit status (0 = success, non-zero = failure)
check_file() {
    local file=$1

    if [ -f "$file" ]; then
        return 0 # success
    else
        return 1 # failure
    fi
}

# Use return value
if check_file "/etc/passwd"; then
    echo "File exists"
else
    echo "File not found"
fi

# Return data through echo
get_double() {
    local num=$1
    echo $((num * 2))
}

result=$(get_double 5)
echo "Result: $result" # Result: 10

Local Variables

# Local variables avoid polluting global scope
my_function() {
    local local_var="I'm local"
    global_var="I'm global"
    echo "Local: $local_var"
}

my_function
echo "Global: $global_var" # outputs: I'm global
echo "Local: $local_var" # outputs nothing (local is gone)

Recursive Functions

# Factorial using recursion
factorial() {
    local n=$1

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

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

# Countdown using recursion
countdown() {
    local n=$1

    if [ $n -gt 0 ]; then
        echo $n
        countdown $((n-1))
    fi
}

countdown 5

Function Error Handling

# Check exit status in function
safe_copy() {
    local src=$1
    local dst=$2

    if [ ! -f "$src" ]; then
        echo "Error: Source file not found: $src" >&2
        return 1
    fi

    cp "$src" "$dst" || return $?
    return 0
}

# Use the function
if safe_copy "file1.txt" "file2.txt"; then
    echo "Copy successful"
else
    echo "Copy failed"
fi

Function Documentation

#!/bin/bash

# Function: add
# Description: Add two numbers
# Arguments:
#   $1 - First number
#   $2 - Second number
# Returns: Sum of the two numbers
add() {
    local a=$1
    local b=$2
    echo $((a + b))
}

result=$(add 5 3)
echo "5 + 3 = $result"

Advanced Patterns

Default Parameters

# Use default value if parameter not provided
greet() {
    local name=${1:-"Guest"}
    local greeting=${2:-"Hello"}
    echo "$greeting, $name!"
}

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

Named Parameters

# Parse named parameters
parse_args() {
    while [[ $# -gt 0 ]]; do
        case $1 in
            -n|--name) name="$2"; shift 2 ;;
            -a|--age) age="$2"; shift 2 ;;
            *) echo "Unknown option: $1"; return 1 ;;
        esac
    done
}

parse_args --name "Alice" --age 30
echo "Name: $name, Age: $age"

Pipeline Functions

# Function that works in pipeline
count_lines() {
    wc -l < /dev/stdin
}

# Use in pipeline
ls -la | count_lines

Function Library

# Create reusable library
# lib.sh
log_info() {
    echo "[INFO] $1"
}

log_error() {
    echo "[ERROR] $1" >&2
}

# Use library in script
source ./lib.sh
log_info "Application started"
log_error "Something went wrong"

Practical Examples

Validation Function

#!/bin/bash

validate_email() {
    local email=$1
    if [[ $email =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
        return 0
    else
        return 1
    fi
}

if validate_email "user@example.com"; then
    echo "Valid email"
else
    echo "Invalid email"
fi

Retry Function

retry() {
    local max_attempts=3
    local attempt=1

    while [ $attempt -le $max_attempts ]; do
        if "$@"; then
            return 0
        fi

        echo "Attempt $attempt failed. Retrying..." >&2
        ((attempt++))
        sleep 2
    done

    return 1
}

retry curl https://example.com

Best Practices

  • Use descriptive function names
  • Always use local for variables in functions
  • Document complex functions with comments
  • Check parameter count at function start
  • Return meaningful exit codes
  • Use consistent naming conventions
  • Keep functions focused and single-purpose
  • Test functions independently
  • Avoid modifying global variables
  • Return data via echo, status via exit code

On this page