Functions

Copy-pasting the same code throughout your script? Functions let you write it once and call it anywhere.

Defining Functions

Two syntaxes (both work):

hljs bash
# Traditional
function greet {
    echo "Hello!"
}

# Shorthand (more common)
greet() {
    echo "Hello!"
}

Calling Functions

hljs bash
#!/bin/bash

greet() {
    echo "Hello, World!"
}

# Call the function
greet
greet
greet
Terminal
$./script.sh
Hello, World! Hello, World! Hello, World!

Define Before Calling

Functions must be defined before they're called. Put them at the top of your script.

Function Arguments

Functions receive arguments just like scripts - $1, $2, etc.:

hljs bash
#!/bin/bash

greet() {
    echo "Hello, $1!"
}

greet "Alice"
greet "Bob"
Terminal
$./greet.sh
Hello, Alice! Hello, Bob!

Multiple Arguments

hljs bash
#!/bin/bash

create_user() {
    local username="$1"
    local email="$2"

    echo "Creating user: $username"
    echo "Email: $email"
}

create_user "john" "john@example.com"

Return Values

Functions return exit codes (0-255), not arbitrary values:

hljs bash
#!/bin/bash

is_root() {
    if [[ "$EUID" -eq 0 ]]; then
        return 0  # success/true
    else
        return 1  # failure/false
    fi
}

if is_root; then
    echo "Running as root"
else
    echo "Not root"
fi

Return Data via echo

For actual values, use command substitution:

hljs bash
#!/bin/bash

get_timestamp() {
    echo "$(date +%Y%m%d_%H%M%S)"
}

timestamp=$(get_timestamp)
echo "Timestamp: $timestamp"

Local Variables

Variables inside functions are global by default. Use local to keep them contained:

hljs bash
#!/bin/bash

test_scope() {
    global_var="I'm global"
    local local_var="I'm local"
}

test_scope

echo "$global_var"   # Works
echo "$local_var"    # Empty - local is gone

Always Use local

Make variables local inside functions unless you specifically need them global. Prevents naming collisions and bugs.

Practical Examples

Logging Function

hljs bash
#!/bin/bash

log() {
    local level="$1"
    local message="$2"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')

    echo "[$timestamp] [$level] $message"
}

log "INFO" "Script started"
log "WARN" "Low disk space"
log "ERROR" "Connection failed"
Terminal
$./script.sh
[2025-01-14 10:30:45] [INFO] Script started [2025-01-14 10:30:45] [WARN] Low disk space [2025-01-14 10:30:45] [ERROR] Connection failed

Validation Function

hljs bash
#!/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

Utility Functions

hljs bash
#!/bin/bash

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

require_root() {
    [[ "$EUID" -eq 0 ]] || die "This script requires root"
}

require_command() {
    command -v "$1" &>/dev/null || die "Required command not found: $1"
}

# Usage
require_root
require_command "curl"
require_command "jq"

echo "All checks passed!"

All Arguments: $@

hljs bash
#!/bin/bash

process_files() {
    for file in "$@"; do
        echo "Processing: $file"
    done
}

process_files file1.txt file2.txt file3.txt
Knowledge Check

What does 'local' do in a function?

Quick Reference

SyntaxPurpose
name() { ... }Define function
nameCall function
$1, $2Function arguments
$@All arguments
local varLocal variable
return 0/1Return exit code
$(function)Capture output

Key Takeaways

  • Functions group reusable code
  • Define before calling
  • Arguments via $1, $2, $@
  • Use local for function variables
  • return sets exit code (0=success)
  • Capture output with $(function_name)
  • Build a library of utility functions

Next: understanding exit codes.