Stage 2 · Tools
Bash Fundamentals
Functions
Function definitions, arguments, return values, and local variables for modular scripts.
Defining Functions
Functions in Bash let you organize code into reusable blocks. There are two syntax forms — the function keyword and the parentheses form. Both work identically.
# Form 1: function keyword
function greet() {
echo "Hello, $1!"
}
# Form 2: parentheses (POSIX-compatible)
greet() {
echo "Hello, $1!"
}
# Calling a function
greet "Alice" # Hello, Alice!Both forms are equivalent in Bash. The parentheses form is POSIX-compatible and works in sh/dash. The function keyword is Bash-specific but more readable.
Use descriptive verb-noun names: check_disk_space, parse_config, deploy_application. Avoid single-letter names or generic names like doStuff. Good function names make scripts self-documenting.
Passing Arguments
Functions receive arguments the same way scripts do — through positional parameters $1, $2, etc. There is no argument count limit, but clarity matters.
backup_file() {
local src="$1"
local dest="{2:-$src.bak}"
if [[ ! -f "$src" ]]; then
echo "Error: $src not found" >&2
return 1
fi
cp "$src" "$dest"
echo "Backed up $src -> $dest"
}
backup_file /etc/hosts # Uses default destination
backup_file /etc/hosts /tmp/h.bak # Custom destinationThe first argument is $1, second is $2, and so on. $0 is the script name, not the function name. $# holds the argument count.
Return Values
Bash functions use two mechanisms for returning data: exit codes (0-255) for success/failure, and command substitution for capturing output. Mixing these up is a common source of bugs.
# Exit code for status
is_running() {
local pid="$1"
kill -0 "$pid" 2>/dev/null
return $? # 0 if process exists, non-zero otherwise
}
if is_running 1234; then
echo "Process 1234 is running"
fi
# Output for data
get_cpu_count() {
nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 1
}
CPUS=$(get_cpu_count)
echo "CPU cores: $CPUS"Use return for exit codes (0 = success). Use echo + command substitution for actual data. Never echo data in functions used for status checks.
Bash return codes are bytes — they wrap around modulo 256. A function returning 256 actually returns 0. For larger values, echo the number and capture it: val=$(my_func).
Local Variables
Without the local keyword, variables inside functions are global. This is a common source of bugs — one function accidentally overwrites a variable used by another.
COUNT=0
increment() {
local COUNT=0 # This is a different variable
COUNT=$((COUNT + 1))
echo "Inside: $COUNT" # 1
}
increment
echo "Outside: $COUNT" # 0 — global was never modified
# Without local, the function would modify the global
increment_broken() {
COUNT=$((COUNT + 1))
}
increment_broken
echo "After broken: $COUNT" # 1 — global was modifiedAlways use local for variables inside functions. This prevents accidental pollution of the global namespace and makes functions truly self-contained.
Function Libraries
Functions can be organized into library files and sourced into scripts. This avoids code duplication and keeps scripts DRY.
#!/usr/bin/env bash
# lib/logging.sh
log() {
local level="$1"
shift
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*"
}
log_info() { log "INFO" "$@"; }
log_warn() { log "WARN" "$@"; }
log_error() { log "ERROR" "$@"; }Library files are just bash scripts with function definitions. They should not have a shebang since they are sourced, not executed.
#!/usr/bin/env bash
# main.sh
SCRIPT_DIR="$(cd "$(dirname "{BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/lib/logging.sh"
log_info "Starting deployment"
log_error "Something went wrong"
log_warn "Disk space low"Use source or . to load a library file. TheBASH_SOURCE trick finds the script's directory regardless of where it is run from.
{BASH_SOURCE[0]} gives the path of the current file, even when the script is sourced. Combined with dirname, it reliably finds sibling files like libraries. This works regardless of the working directory.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.