Function Basics
Functions allow you to group commands into reusable blocks. They make scripts more organized, readable, and maintainable.
Why Use Functions?
- Reusability: Write once, use many times
- Organization: Break complex scripts into manageable pieces
- Readability: Give meaningful names to code blocks
- Maintenance: Fix bugs in one place
Defining Functions
Two equivalent syntaxes:
# Syntax 1: Using function keyword
function greet {
echo "Hello, World!"
}
# Syntax 2: Using parentheses (POSIX compatible)
greet() {
echo "Hello, World!"
}
The parentheses syntax is more portable and commonly used.
Calling Functions
Simply use the function name:
#!/bin/bash
greet() {
echo "Hello!"
}
# Call the function
greet
greet
greet
Function Order Matters
Functions must be defined before they're called:
#!/bin/bash
# WRONG - function not defined yet
greet # Error: command not found
greet() {
echo "Hello!"
}
#!/bin/bash
# CORRECT - define first, then call
greet() {
echo "Hello!"
}
greet # Works!
Exercise: Create a Function
Define and call a simple function:
Multi-Line Functions
For longer functions:
#!/bin/bash
backup_files() {
echo "Starting backup..."
mkdir -p /backup
cp -r /home/user/documents /backup/
echo "Backup complete!"
}
backup_files
Functions with Commands
Functions can contain any valid bash commands:
#!/bin/bash
show_system_info() {
echo "=== System Information ==="
echo "Hostname: $(hostname)"
echo "User: $USER"
echo "Date: $(date)"
echo "Uptime: $(uptime)"
}
show_system_info
One-Liner Functions
For simple functions:
# Single command
greet() { echo "Hello, $1!"; }
# Multiple commands (semicolons)
setup() { mkdir -p logs; touch logs/app.log; echo "Setup complete"; }
Function Naming
Best practices for function names:
# Good names - descriptive verbs
process_files()
validate_input()
send_email()
calculate_total()
# Conventions
get_user_name() # Lowercase with underscores
getUserName() # camelCase (less common in bash)
# Bad names
f1() # Too short, meaningless
do_stuff() # Too vague
Checking if Function Exists
#!/bin/bash
# Check if function is defined
if type greet &>/dev/null; then
echo "greet function exists"
else
echo "greet function not defined"
fi
# Or use declare
if declare -f greet > /dev/null; then
echo "greet is a function"
fi
Listing All Functions
# List all function names
declare -F
# List function definitions
declare -f
# List specific function
declare -f function_name
Undefining Functions
Remove a function with unset:
#!/bin/bash
greet() {
echo "Hello!"
}
greet # Works
unset -f greet
greet # Error: command not found
Exercise: Multiple Functions
Create and use multiple functions:
Functions in Scripts
Organize scripts with functions:
#!/bin/bash
# Well-organized script structure
#--- Configuration ---
LOG_FILE="/var/log/app.log"
#--- Functions ---
log_message() {
echo "[$(date)] $1" >> "$LOG_FILE"
}
show_usage() {
echo "Usage: $0 [start|stop|status]"
}
start_service() {
log_message "Starting service"
echo "Service started"
}
stop_service() {
log_message "Stopping service"
echo "Service stopped"
}
#--- Main Script ---
case "${1:-}" in
start) start_service ;;
stop) stop_service ;;
*) show_usage ;;
esac
Function Libraries
Source functions from external files:
# functions.sh - Library file
log() { echo "[LOG] $1"; }
warn() { echo "[WARN] $1" >&2; }
error() { echo "[ERROR] $1" >&2; exit 1; }
# main.sh - Use the library
#!/bin/bash
source ./functions.sh
log "Starting application"
warn "This is a warning"
# error "This would exit"
Recursive Functions
Functions can call themselves:
#!/bin/bash
countdown() {
local num=$1
if [ $num -le 0 ]; then
echo "Liftoff!"
return
fi
echo "$num..."
countdown $((num - 1))
}
countdown 5
Caution: Always have a base case to prevent infinite recursion!
Best Practices
- Define before use: Functions must be defined before called
- Use descriptive names:
validate_emailnotve - Keep functions focused: Each function does one thing
- Document complex functions: Add comments explaining purpose
- Use local variables: Avoid polluting global namespace
- Return meaningful exit codes: 0 for success
#!/bin/bash
# Good function example
# Validates an email address format
# Arguments: $1 - email to validate
# Returns: 0 if valid, 1 if invalid
validate_email() {
local email="$1"
if [[ -z "$email" ]]; then
return 1
fi
if [[ "$email" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}$ ]]; then
return 0
fi
return 1
}
Key Takeaways
- Functions group commands for reuse:
name() { commands; } - Define functions before calling them
- Call functions by name:
function_name - Use descriptive, verb-based names
- Functions help organize and maintain scripts
- Source external files to create function libraries
- Use
unset -fto remove a function - Always document complex functions

