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
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
#!/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
$ ./disk_check.sh
✓ Disk usage is healthy at 45%
$ ./disk_check.sh # When usage is high
⚠️ WARNING: Disk usage is at 92%
Threshold: 80%
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
#!/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"
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
#!/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
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%"
#!/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"
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
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
Comments (0)
This is exactly what I needed! The initContainer approach solved our migration issues completely. Thanks for the detailed guide!
ReplyLeave a Comment