9.4. Generating Reports#

9.4.1. Common Pitfalls#

1. Using echo instead of printf for formatted output

# Less control with echo
echo "Name: $name, Age: $age"

# Better: printf for precise formatting
printf "Name: %-20s Age: %3d\n" "$name" "$age"

2. Not including timestamps in logs

# Bad: hard to track when events occurred
echo "Error occurred" >> app.log

# Good: timestamp helps debugging
echo "$(date '+%Y-%m-%d %H:%M:%S') Error occurred" >> app.log

3. Mixing stdout and stderr in the same file without distinction

# Hard to separate errors from normal output
command > all_output.log 2>&1

# Better: keep them separate initially
command > output.log 2> errors.log

4. Forgetting to handle log rotation

# Bad: log grows unbounded
echo "Message" >> app.log

# Better: rotate when size limit reached
# Or use logrotate utility with proper configuration

9.4.2. Building Complex Reports#

9.4.2.1. Multi-Section Report Generation#

#!/bin/bash

# Generate comprehensive system report
generate_system_report() {
  local report_file=$1
  local report_date=$(date '+%Y-%m-%d %H:%M:%S')
  
  {
    # Title section
    printf "\n"
    printf "%-60s\n" "============ SYSTEM REPORT ============"
    printf "%-60s\n" "Generated: $report_date"
    printf "%-60s\n" "Host: $(hostname)"
    printf "%-60s\n" "======================================="
    
    # Hardware section
    printf "\n%-20s\n" "=== HARDWARE ==="
    printf "%-15s : %s\n" "CPU Model" "$(lscpu | grep 'Model name' | cut -d: -f2-)"
    printf "%-15s : %d cores\n" "CPU Count" "$(nproc)"
    printf "%-15s : %s\n" "Total Memory" "$(free -h | awk 'NR==2 {print $2}')"
    
    # Disk section
    printf "\n%-20s\n" "=== DISK USAGE ==="
    printf "%-20s %-10s %-10s %s\n" "Filesystem" "Size" "Used" "Percent"
    df -h | tail -n +2 | awk '{printf "%-20s %-10s %-10s %s\n", $1, $2, $3, $5}'
    
    # Process section
    printf "\n%-20s\n" "=== TOP PROCESSES ==="
    printf "%-8s %-8s %-15s %s\n" "PID" "CPU" "MEMORY" "COMMAND"
    ps aux | sort -k3 -rn | head -5 | awk '{printf "%-8s %-8s %-15s %s\n", $2, $3, $4, $11}'
    
    printf "\n%-60s\n" "======================================="
  } | tee "$report_file"
}

generate_system_report system_report.txt

9.4.3. Generating CSV and Tabular Data#

9.4.3.1. Creating CSV Output#

#!/bin/bash

# Export data to CSV
export_users_csv() {
  local output=$1
  
  {
    # Header
    printf "username,uid,shell\n"
    
    # Data rows (from /etc/passwd)
    awk -F: '{printf "%s,%d,%s\n", $1, $3, $7}' /etc/passwd
  } > "$output"
}

# Usage
export_users_csv users.csv

9.4.3.2. Creating Formatted Tables#

#!/bin/bash

# Generate a process table
generate_process_table() {
  local output=$1
  
  {
    # Header with alignment
    printf "%-8s %-8s %-10s %8s\n" "PID" "USER" "COMMAND" "MEMORY"
    printf "%-8s %-8s %-10s %8s\n" "---" "----" "-------" "------"
    
    # Process data
    ps aux | sort -k6 -rn | head -10 | \
      awk '{printf "%-8s %-8s %-10s %8s\n", $2, $1, $11, $6}'
  } > "$output"
}

generate_process_table process_report.txt

9.4.4. Creating Structured Logs#

9.4.4.1. Log File Management#

#!/bin/bash

LOG_DIR="/var/log/myapp"
LOG_FILE="$LOG_DIR/app.log"
ERROR_LOG="$LOG_DIR/app.err"

# Initialize logging
setup_logging() {
  mkdir -p "$LOG_DIR"
  touch "$LOG_FILE" "$ERROR_LOG"
}

# Log function with timestamp
log() {
  local level=$1
  shift
  local message=$*
  local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
  
  case $level in
    INFO)
      echo "[$timestamp] INFO: $message" >> "$LOG_FILE"
      ;;
    ERROR)
      echo "[$timestamp] ERROR: $message" | tee -a "$ERROR_LOG" >&2
      ;;
    WARN)
      echo "[$timestamp] WARN: $message" >> "$LOG_FILE"
      ;;
  esac
}

# Usage
setup_logging
log INFO "Application started"
log WARN "This is a warning"
log ERROR "An error occurred"

9.4.4.2. Log Rotation#

#!/bin/bash

# Rotate logs when they exceed size limit
rotate_logs() {
  local log_file=$1
  local max_size=$((10 * 1024 * 1024))  # 10MB
  
  if [[ -f "$log_file" ]] && [[ $(stat -c%s "$log_file") -gt $max_size ]]; then
    local timestamp=$(date +%Y%m%d_%H%M%S)
    mv "$log_file" "${log_file}.${timestamp}"
    gzip "${log_file}.${timestamp}"
    touch "$log_file"
  fi
}

rotate_logs /var/log/myapp/app.log

9.4.5. Structured Output Formatting#

9.4.5.1. Using printf for Precise Formatting#

# Basic formatting
printf "Name: %s, Age: %d\n" "Alice" 30

# Column alignment
printf "%-15s %10s %8s\n" "Item" "Quantity" "Price"
printf "%-15s %10d %8.2f\n" "Apple" 5 1.50
printf "%-15s %10d %8.2f\n" "Banana" 3 0.75

# Padding and alignment options
printf "%5d\n" 42        # Right-align in 5-char width
printf "%-5d\n" 42       # Left-align in 5-char width
printf "%05d\n" 42       # Zero-padded to 5 chars

# Floating point precision
printf "%.2f\n" 3.14159  # 2 decimal places
printf "%8.2f\n" 3.14159 # 8-char width, 2 decimals

9.4.5.2. Creating Formatted Reports#

#!/bin/bash

# Generate a system report
generate_report() {
  local output_file=$1
  
  {
    printf "====================================\n"
    printf "%-20s %s\n" "System Report" "$(date)"
    printf "====================================\n\n"
    
    printf "%-20s %s\n" "Hostname:" "$(hostname)"
    printf "%-20s %s\n" "Kernel:" "$(uname -r)"
    printf "%-20s %d\n" "CPU Cores:" "$(nproc)"
    printf "%-20s %s\n" "Disk Usage:" "$(df -h / | awk 'NR==2 {print $5}')"
    printf "%-20s %s\n" "Memory Usage:" "$(free -h | awk 'NR==2 {print $3 "/" $2}')"
    printf "\n====================================\n"
  } | tee "$output_file"
}

generate_report system_report.txt