Simple scripts run commands in order. Intelligent scripts make decisions, repeat actions, and reuse code. This lesson transforms you from a script runner into a script writer. You'll learn the programming fundamentals that make Bash a real language—conditionals for decision making, loops for repetition, and functions for organization.

1. Learning Objectives

By the end of this lesson, you will be able to:

  • Use if/elif/else statements for decision making
  • Compare strings and numbers with test operators
  • Loop through files with for loops
  • Create infinite loops with while for monitoring
  • Write reusable functions
  • Build a complete server health check script

2. Why This Matters

Real-world scenario 1: Your script needs to check if a file exists before deleting it. Without conditionals, you'd delete blindly and crash when the file is missing.

Real-world scenario 2: You need to process 1000 log files. Without loops, you'd write 1000 commands. With a for loop, you write one.

Real-world scenario 3: You're writing the same backup logic in 5 different scripts. Without functions, you copy-paste and update in 5 places. With a function, you write once and call it everywhere.

3. Core Concepts

Test Operators: Asking Questions

Before you can make decisions, you need to ask questions. Test operators let you check conditions.

File Tests (Most Common in DevOps)

String Tests

Numeric Tests

#!/bin/bash
# Test operators in action

# File tests
if [ -f "/etc/passwd" ]; then
    echo "✓ /etc/passwd exists"
fi

if [ -d "/var/log" ]; then
    echo "✓ /var/log is a directory"
fi

# String tests
name="Alex"
if [ "$name" = "Alex" ]; then
    echo "✓ Name is Alex"
fi

# Numeric tests
age=30
if [ "$age" -gt 18 ]; then
    echo "✓ Age is greater than 18"
fi
Testing different conditions

If/Else Statements: Making Decisions

#!/bin/bash
# Basic if statement

if [ condition ]; then
    # Runs if condition is true
    echo "Condition is true"
fi

# If/Else
if [ condition ]; then
    echo "True"
else
    echo "False"
fi

# If/Elif/Else (multiple conditions)
if [ condition1 ]; then
    echo "Condition 1 is true"
elif [ condition2 ]; then
    echo "Condition 2 is true"
else
    echo "No conditions true"
fi
If statement syntax
#!/bin/bash
# Practical example: Check disk space

threshold=80
usage=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')

if [ "$usage" -gt "$threshold" ]; then
    echo "⚠️ WARNING: Disk usage is at ${usage}%"
    echo "Threshold: ${threshold}%"
    # Could send alert here
else
    echo "✓ Disk usage is healthy at ${usage}%"
fi
Real-world: Disk space monitor
kubectl get pods -w
$ ./disk_check.sh
✓ Disk usage is healthy at 45%

$ ./disk_check.sh # When usage is high
⚠️ WARNING: Disk usage is at 92%
Threshold: 80%
Conditional alert based on disk usage

For Loops: Processing Lists

#!/bin/bash
# For loop syntax

# Loop through a list of items
for item in apple banana orange; do
    echo "Fruit: $item"
done

# Loop through files
for file in *.txt; do
    echo "Processing: $file"
    wc -l "$file"
done

# Loop through numbers (C-style)
for ((i=1; i<=5; i++)); do
    echo "Number: $i"
done

# Loop through command output
for user in $(cut -d: -f1 /etc/passwd | head -5); do
    echo "User: $user"
done
Different for loop patterns
#!/bin/bash
# Practical: Batch rename files

counter=1
for file in *.jpg; do
    new_name="image_${counter}.jpg"
    mv "$file" "$new_name"
    echo "Renamed: $file → $new_name"
    ((counter++))
done
echo "Renamed $((counter-1)) files"
Batch rename script

While Loops: Repeating Until Condition Changes

#!/bin/bash
# While loop syntax

counter=1
while [ "$counter" -le 5 ]; do
    echo "Counter: $counter"
    ((counter++))
done

# Read file line by line
while IFS= read -r line; do
    echo "Line: $line"
done < "data.txt"

# Infinite loop (until condition changes)
while true; do
    echo "Running... Press Ctrl+C to stop"
    sleep 5
done
While loop patterns
#!/bin/bash
# Practical: Wait for service to be ready

service_url="http://localhost:8080/health"
max_attempts=30
attempt=1

while [ "$attempt" -le "$max_attempts" ]; do
    echo "Attempt $attempt: Checking $service_url"
    
    if curl -s --fail "$service_url" > /dev/null; then
        echo "✓ Service is ready!"
        exit 0
    fi
    
    echo "Service not ready yet. Waiting 2 seconds..."
    sleep 2
    ((attempt++))
done

echo "✗ Service failed to start after $max_attempts attempts"
exit 1
Service health check with retry logic

Functions: Reusable Code Blocks

#!/bin/bash
# Function syntax

# Define a function
function greet() {
    echo "Hello, $1!"  # $1 is first argument
}

# Call the function
greet "Alex"

# Function with multiple parameters
function log_message() {
    local level="$1"
    local message="$2"
    echo "[$level] $(date): $message"
}

log_message "INFO" "Backup completed"
log_message "ERROR" "Disk space low"

# Function that returns a value (via echo)
get_disk_usage() {
    df -h / | awk 'NR==2 {print $5}' | sed 's/%//'
}

usage=$(get_disk_usage)
echo "Disk usage: $usage%"
Creating and using functions
#!/bin/bash
# Complete function library for DevOps

# Color output function
print_success() { echo -e "\033[0;32m✓ $1\033[0m"; }
print_error() { echo -e "\033[0;31m✗ $1\033[0m"; }
print_warning() { echo -e "\033[1;33m⚠️ $1\033[0m"; }
print_info() { echo -e "\033[0;34mℹ️ $1\033[0m"; }

# Check if command exists
command_exists() {
    command -v "$1" >/dev/null 2>&1
}

# Check if service is running
is_service_running() {
    systemctl is-active --quiet "$1"
    return $?
}

# Logging function
log() {
    local level="$1"
    local message="$2"
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $message" >> /var/log/script.log
}

# Usage examples
print_success "Docker installed"
print_error "Connection failed"
print_warning "Disk space low"

if command_exists "docker"; then
    print_success "Docker is available"
fi

log "INFO" "Script execution started"
Production-ready function library

4. Complete Project: Server Health Check Script

#!/bin/bash
# server_health.sh - Comprehensive server health check

# Color codes
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

# Thresholds
CPU_THRESHOLD=80
MEMORY_THRESHOLD=80
DISK_THRESHOLD=80

# Function: Check CPU usage
check_cpu() {
    cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
    cpu_usage=${cpu_usage%.*}
    
    if [ "$cpu_usage" -gt "$CPU_THRESHOLD" ]; then
        echo -e "${RED}✗ CPU: ${cpu_usage}% (Threshold: ${CPU_THRESHOLD}%)${NC}"
        return 1
    else
        echo -e "${GREEN}✓ CPU: ${cpu_usage}%${NC}"
        return 0
    fi
}

# Function: Check memory usage
check_memory() {
    memory_usage=$(free | grep Mem | awk '{print ($3/$2) * 100.0}' | cut -d. -f1)
    
    if [ "$memory_usage" -gt "$MEMORY_THRESHOLD" ]; then
        echo -e "${RED}✗ Memory: ${memory_usage}% (Threshold: ${MEMORY_THRESHOLD}%)${NC}"
        return 1
    else
        echo -e "${GREEN}✓ Memory: ${memory_usage}%${NC}"
        return 0
    fi
}

# Function: Check disk usage
check_disk() {
    failed=0
    while IFS= read -r line; do
        usage=$(echo "$line" | awk '{print $5}' | sed 's/%//')
        mount=$(echo "$line" | awk '{print $6}')
        
        if [ "$usage" -gt "$DISK_THRESHOLD" ]; then
            echo -e "${RED}✗ Disk $mount: ${usage}% (Threshold: ${DISK_THRESHOLD}%)${NC}"
            failed=1
        else
            echo -e "${GREEN}✓ Disk $mount: ${usage}%${NC}"
        fi
    done < <(df -h | grep '^/dev/')
    
    return $failed
}

# Function: Check running services
check_services() {
    services=("docker" "nginx" "sshd")
    
    for service in "${services[@]}"; do
        if systemctl is-active --quiet "$service"; then
            echo -e "${GREEN}✓ $service is running${NC}"
        else
            echo -e "${RED}✗ $service is not running${NC}"
        fi
    done
}

# Function: Check recent errors in logs
check_logs() {
    errors=$(journalctl -p 3 -n 5 --no-pager 2>/dev/null | grep -c "error\|failed" || echo "0")
    
    if [ "$errors" -gt 0 ]; then
        echo -e "${YELLOW}⚠️ Found $errors recent errors in logs${NC}"
    else
        echo -e "${GREEN}✓ No recent errors found${NC}"
    fi
}

# Main execution
echo "========================================="
echo "    SERVER HEALTH CHECK REPORT"
echo "========================================="
echo "Hostname: $(hostname)"
echo "Date: $(date)"
echo ""

echo "--- CPU & Memory ---"
check_cpu
check_memory
echo ""

echo "--- Disk Usage ---"
check_disk
echo ""

echo "--- Running Services ---"
check_services
echo ""

echo "--- Log Analysis ---"
check_logs
echo ""

# Summary
if [ $? -eq 0 ]; then
    echo -e "${GREEN}✓ Overall Status: HEALTHY${NC}"
    exit 0
else
    echo -e "${RED}✗ Overall Status: ISSUES DETECTED${NC}"
    exit 1
fi
Complete server health check script with functions

5. Common Errors & Solutions

6. Summary Checklist

7. Next Steps

Next category: Git & GitHub - Version Control

You'll learn to:

  • Initialize repositories and make commits
  • Create branches for features and bugfixes
  • Collaborate with GitHub
  • Resolve merge conflicts

Gataya Med

DevOps Engineer & Backend Developer. Sharing insights on cloud, automation, and scalable systems.

Comments (0)

Sarah Chen January 18, 2025

This is exactly what I needed! The initContainer approach solved our migration issues completely. Thanks for the detailed guide!

Reply

Leave a Comment