Shell Script Functions: Complete Guide and Reference
Table of Contents
1. [Introduction to Shell Script Functions](#introduction) 2. [Function Syntax and Declaration](#syntax) 3. [Function Parameters and Arguments](#parameters) 4. [Return Values and Exit Codes](#return-values) 5. [Variable Scope in Functions](#variable-scope) 6. [Advanced Function Concepts](#advanced-concepts) 7. [Best Practices](#best-practices) 8. [Common Use Cases](#use-cases) 9. [Troubleshooting](#troubleshooting) 10. [Examples and Practical Applications](#examples)Introduction to Shell Script Functions {#introduction}
Shell script functions are reusable blocks of code that perform specific tasks within a shell script. They provide modularity, code reusability, and better organization of complex scripts. Functions help reduce code duplication and make scripts easier to maintain and debug.
Why Use Functions in Shell Scripts
| Benefit | Description | |---------|-------------| | Code Reusability | Write once, use multiple times throughout the script | | Modularity | Break complex tasks into smaller, manageable pieces | | Maintainability | Easier to update and modify specific functionality | | Readability | Makes scripts more organized and easier to understand | | Testing | Individual functions can be tested separately | | Debugging | Isolate issues to specific functions |
Function Syntax and Declaration {#syntax}
Basic Function Syntax
There are several ways to declare functions in shell scripts:
#### Method 1: Using the function keyword
`bash
function function_name() {
# Function body
commands
return [exit_code]
}
`
#### Method 2: POSIX-compliant syntax
`bash
function_name() {
# Function body
commands
return [exit_code]
}
`
#### Method 3: Alternative syntax
`bash
function function_name {
# Function body
commands
return [exit_code]
}
`
Function Declaration Rules
| Rule | Description | Example |
|------|-------------|---------|
| Naming | Function names must start with letter/underscore | my_function, _helper, calculateSum |
| Case Sensitivity | Function names are case-sensitive | MyFunc and myfunc are different |
| No Spaces | No spaces around parentheses in POSIX syntax | func() not func () |
| Declaration Before Use | Functions must be declared before calling | Define at top of script |
Basic Function Example
`bash
#!/bin/bash
Simple function declaration
greet() { echo "Hello, World!" }Function call
greet`Output:
`
Hello, World!
`
Function Parameters and Arguments {#parameters}
Passing Arguments to Functions
Functions can accept arguments similar to how scripts accept command-line arguments.
#### Positional Parameters in Functions
| Parameter | Description | Example Usage |
|-----------|-------------|---------------|
| $0 | Script name (not function name) | echo "Script: $0" |
| $1, $2, $3... | First, second, third argument | echo "First arg: $1" |
| $# | Number of arguments passed | if [ $# -eq 2 ] |
| $@ | All arguments as separate words | for arg in "$@" |
| $ | All arguments as single string | echo "Args: $" |
#### Function with Parameters Example
`bash
#!/bin/bash
Function that accepts parameters
greet_user() { local name=$1 local age=$2 if [ $# -lt 2 ]; then echo "Usage: greet_userFunction calls
greet_user "Alice" 25 greet_user "Bob" # This will show usage message`Output:
`
Hello, Alice! You are 25 years old.
Usage: greet_user `
Advanced Parameter Handling
#### Default Parameter Values
`bash
#!/bin/bash
Function with default parameters
create_file() { local filename=${1:-"default.txt"} local content=${2:-"Default content"} echo "$content" > "$filename" echo "Created file: $filename" }Usage examples
create_file # Uses all defaults create_file "custom.txt" # Custom filename, default content create_file "data.txt" "Custom data" # Custom filename and content`#### Variable Number of Arguments
`bash
#!/bin/bash
Function that processes multiple arguments
calculate_sum() { local sum=0 local count=0 # Process all arguments for number in "$@"; do if [[ $number =~ ^[0-9]+$ ]]; then sum=$((sum + number)) count=$((count + 1)) else echo "Warning: '$number' is not a valid number" fi done echo "Sum of $count numbers: $sum" return 0 }Usage
calculate_sum 10 20 30 40 calculate_sum 5 abc 15 xyz 25`Output:
`
Sum of 4 numbers: 100
Warning: 'abc' is not a valid number
Warning: 'xyz' is not a valid number
Sum of 2 numbers: 45
`
Return Values and Exit Codes {#return-values}
Understanding Return Values
Functions in shell scripts return exit codes (0-255) rather than traditional return values. The return statement sets the exit status of the function.
#### Return Code Conventions
| Return Code | Meaning | Usage |
|-------------|---------|--------|
| 0 | Success | Function completed successfully |
| 1 | General error | Generic failure condition |
| 2 | Misuse of shell command | Invalid arguments or usage |
| 126 | Command cannot execute | Permission problem |
| 127 | Command not found | Function or command doesn't exist |
| 128+n | Fatal error signal "n" | Script terminated by signal |
#### Function Return Example
`bash
#!/bin/bash
Function that validates input and returns appropriate codes
validate_email() { local email=$1 # Check if email parameter is provided if [ -z "$email" ]; then echo "Error: Email address required" return 1 fi # Basic email validation if [[ $email =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then echo "Valid email: $email" return 0 else echo "Invalid email format: $email" return 2 fi }Test the function and check return codes
validate_email "user@example.com" echo "Return code: $?"validate_email "invalid-email" echo "Return code: $?"
validate_email
echo "Return code: $?"
`
Output:
`
Valid email: user@example.com
Return code: 0
Invalid email format: invalid-email
Return code: 2
Error: Email address required
Return code: 1
`
Capturing Function Output
#### Using Command Substitution
`bash
#!/bin/bash
Function that generates output
get_system_info() { local info_type=$1 case $info_type in "hostname") hostname ;; "user") whoami ;; "date") date +"%Y-%m-%d %H:%M:%S" ;; *) echo "Unknown info type: $info_type" return 1 ;; esac }Capture function output
current_host=$(get_system_info "hostname") current_user=$(get_system_info "user") current_date=$(get_system_info "date")echo "System Information:"
echo "Host: $current_host"
echo "User: $current_user"
echo "Date: $current_date"
`
Variable Scope in Functions {#variable-scope}
Global vs Local Variables
Understanding variable scope is crucial for writing maintainable shell functions.
#### Variable Scope Types
| Scope Type | Declaration | Accessibility | Lifetime |
|------------|-------------|---------------|----------|
| Global | variable=value | Entire script | Until script ends |
| Local | local variable=value | Function only | Until function returns |
| Environment | export variable=value | Child processes | Until session ends |
#### Variable Scope Example
`bash
#!/bin/bash
Global variable
global_var="I am global"demonstrate_scope() { local local_var="I am local" local global_var="I am local override" # Shadows global variable echo "Inside function:" echo " Local variable: $local_var" echo " Global variable (shadowed): $global_var" # Modify a global variable another_global="Set inside function" }
echo "Before function call:" echo " Global variable: $global_var" echo " Another global: ${another_global:-'(not set)'}"
demonstrate_scope
echo "After function call:"
echo " Global variable: $global_var" # Original value preserved
echo " Local variable: ${local_var:-'(not accessible)'}"
echo " Another global: $another_global" # Set by function
`
Output:
`
Before function call:
Global variable: I am global
Another global: (not set)
Inside function:
Local variable: I am local
Global variable (shadowed): I am local override
After function call:
Global variable: I am global
Local variable: (not accessible)
Another global: Set inside function
`
Best Practices for Variable Scope
#### Always Use Local Variables
`bash
#!/bin/bash
Good practice: Use local variables
process_data() { local input_file=$1 local output_file=$2 local temp_file="/tmp/process_$" local line_count=0 while IFS= read -r line; do # Process line echo "Processed: $line" >> "$temp_file" line_count=$((line_count + 1)) done < "$input_file" mv "$temp_file" "$output_file" echo "Processed $line_count lines" }Bad practice: Using global variables
process_data_bad() { input_file=$1 # Global variable output_file=$2 # Global variable temp_file="/tmp/process_$" # Global variable # ... rest of function }`Advanced Function Concepts {#advanced-concepts}
Recursive Functions
Functions can call themselves, creating recursive behavior.
#### Recursive Function Example
`bash
#!/bin/bash
Recursive function to calculate factorial
factorial() { local n=$1 # Base case if [ $n -le 1 ]; then echo 1 return 0 fi # Recursive case local prev_result=$(factorial $((n - 1))) echo $((n * prev_result)) }Calculate factorial of 5
result=$(factorial 5) echo "5! = $result"Recursive directory listing
list_directory() { local dir=$1 local depth=${2:-0} local indent="" # Create indentation for ((i=0; iFunction Arrays and Complex Data Structures
#### Passing Arrays to Functions
`bash
#!/bin/bash
Function that processes an array
process_array() { local -n arr_ref=$1 # Name reference to array local operation=$2 case $operation in "sum") local sum=0 for element in "${arr_ref[@]}"; do sum=$((sum + element)) done echo "Sum: $sum" ;; "max") local max=${arr_ref[0]} for element in "${arr_ref[@]}"; do if [ $element -gt $max ]; then max=$element fi done echo "Max: $max" ;; "count") echo "Count: ${#arr_ref[@]}" ;; esac }Test with array
numbers=(10 25 3 47 15 8) process_array numbers "sum" process_array numbers "max" process_array numbers "count"`Function Libraries and Sourcing
#### Creating Function Libraries
Create a separate file for common functions:
File: lib/common_functions.sh
`bash
#!/bin/bash
Logging functions
log_info() { echo "[INFO $(date '+%Y-%m-%d %H:%M:%S')] $*" }log_error() { echo "[ERROR $(date '+%Y-%m-%d %H:%M:%S')] $*" >&2 }
log_warn() { echo "[WARN $(date '+%Y-%m-%d %H:%M:%S')] $*" }
File operations
backup_file() { local file=$1 local backup_dir=${2:-"./backup"} if [ ! -f "$file" ]; then log_error "File not found: $file" return 1 fi mkdir -p "$backup_dir" cp "$file" "$backup_dir/$(basename "$file").$(date +%Y%m%d_%H%M%S)" log_info "Backed up $file" }Network functions
check_connectivity() { local host=${1:-"google.com"} local timeout=${2:-5} if ping -c 1 -W $timeout "$host" >/dev/null 2>&1; then log_info "Connectivity to $host: OK" return 0 else log_error "Connectivity to $host: FAILED" return 1 fi }`Main script using the library:
`bash
#!/bin/bash
Source the function library
source "lib/common_functions.sh"Use library functions
log_info "Starting application" backup_file "/etc/passwd" "/tmp/backup" check_connectivity "example.com" log_info "Application completed"`Best Practices {#best-practices}
Function Design Principles
| Principle | Description | Example |
|-----------|-------------|---------|
| Single Responsibility | Each function should do one thing well | validate_email() vs process_user_data() |
| Clear Naming | Function names should describe what they do | calculate_tax() vs calc() |
| Parameter Validation | Always validate input parameters | Check for required arguments |
| Error Handling | Handle errors gracefully | Return appropriate exit codes |
| Documentation | Comment complex functions | Explain parameters and return values |
Error Handling in Functions
`bash
#!/bin/bash