Intermediate Bash Examples
This section contains more advanced Bash scripting examples that demonstrate intermediate concepts and real-world applications.
Function Examples
Library of Utility Functions
#!/bin/bash
# utils.sh - Utility functions library
# Logging function with levels
log() {
local level="$1"
shift
local message="$*"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
case "$level" in
ERROR) echo "[$timestamp] ERROR: $message" >&2 ;;
WARN) echo "[$timestamp] WARN: $message" ;;
INFO) echo "[$timestamp] INFO: $message" ;;
DEBUG) [ "$DEBUG" = "1" ] && echo "[$timestamp] DEBUG: $message" ;;
esac
}
# Check if command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Validate email format
validate_email() {
local email="$1"
[[ $email =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]
}
# Get file extension
get_extension() {
local filename="$1"
echo "${filename##*.}"
}
# Convert bytes to human readable format
bytes_to_human() {
local bytes="$1"
local units=("B" "KB" "MB" "GB" "TB")
local unit=0
while [ $bytes -gt 1024 ] && [ $unit -lt 4 ]; do
bytes=$((bytes / 1024))
((unit++))
done
echo "$bytes ${units[$unit]}"
}
# Create backup with timestamp
backup_file() {
local file="$1"
local backup_dir="${2:-./backups}"
if [ ! -f "$file" ]; then
log ERROR "File '$file' does not exist"
return 1
fi
mkdir -p "$backup_dir"
local backup_name="$(basename "$file").backup.$(date +%Y%m%d_%H%M%S)"
if cp "$file" "$backup_dir/$backup_name"; then
log INFO "Backup created: $backup_dir/$backup_name"
echo "$backup_dir/$backup_name"
return 0
else
log ERROR "Failed to create backup"
return 1
fi
}
# Example usage
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
# Test the functions
log INFO "Testing utility functions"
if command_exists "git"; then
log INFO "Git is available"
else
log WARN "Git is not installed"
fi
if validate_email "user@example.com"; then
log INFO "Email format is valid"
else
log ERROR "Invalid email format"
fi
echo "File extension of script.sh: $(get_extension "script.sh")"
echo "1048576 bytes = $(bytes_to_human 1048576)"
fiConfiguration Manager
#!/bin/bash
# config_manager.sh - Configuration file manager
CONFIG_FILE="app.conf"
# Default configuration
declare -A config=(
[app_name]="MyApp"
[version]="1.0.0"
[debug]="false"
[port]="8080"
[database_host]="localhost"
[database_port]="5432"
[log_level]="INFO"
)
# Load configuration from file
load_config() {
if [ -f "$CONFIG_FILE" ]; then
while IFS='=' read -r key value; do
# Skip comments and empty lines
[[ $key =~ ^[[:space:]]*# ]] && continue
[[ -z $key ]] && continue
# Remove leading/trailing whitespace
key=$(echo "$key" | xargs)
value=$(echo "$value" | xargs)
config["$key"]="$value"
done < "$CONFIG_FILE"
echo "Configuration loaded from $CONFIG_FILE"
else
echo "Configuration file not found, using defaults"
fi
}
# Save configuration to file
save_config() {
echo "# Application Configuration" > "$CONFIG_FILE"
echo "# Generated on $(date)" >> "$CONFIG_FILE"
echo >> "$CONFIG_FILE"
for key in "${!config[@]}"; do
echo "$key=${config[$key]}" >> "$CONFIG_FILE"
done
echo "Configuration saved to $CONFIG_FILE"
}
# Get configuration value
get_config() {
local key="$1"
echo "${config[$key]}"
}
# Set configuration value
set_config() {
local key="$1"
local value="$2"
config["$key"]="$value"
echo "Set $key = $value"
}
# Show all configuration
show_config() {
echo "Current Configuration:"
echo "====================="
for key in "${!config[@]}"; do
printf "%-20s = %s\n" "$key" "${config[$key]}"
done
}
# Interactive configuration editor
edit_config() {
while true; do
echo
echo "Configuration Editor"
echo "==================="
echo "1. Show current config"
echo "2. Edit a value"
echo "3. Save config"
echo "4. Load config"
echo "5. Exit"
read -p "Choose option: " choice
case $choice in
1) show_config ;;
2)
read -p "Enter key to edit: " key
if [[ -n "${config[$key]}" ]]; then
echo "Current value: ${config[$key]}"
read -p "Enter new value: " value
set_config "$key" "$value"
else
echo "Key '$key' not found"
fi
;;
3) save_config ;;
4) load_config ;;
5) break ;;
*) echo "Invalid choice" ;;
esac
done
}
# Main execution
load_config
if [ $# -eq 0 ]; then
edit_config
else
case "$1" in
get) get_config "$2" ;;
set) set_config "$2" "$3" ;;
show) show_config ;;
save) save_config ;;
load) load_config ;;
*) echo "Usage: $0 {get|set|show|save|load} [key] [value]" ;;
esac
fiDatabase Backup Script
#!/bin/bash
# db_backup.sh - Database backup script with rotation
# Configuration
DB_HOST="${DB_HOST:-localhost}"
DB_PORT="${DB_PORT:-5432}"
DB_USER="${DB_USER:-postgres}"
DB_NAME="${DB_NAME:-myapp}"
BACKUP_DIR="${BACKUP_DIR:-/backup/database}"
RETENTION_DAYS="${RETENTION_DAYS:-7}"
COMPRESSION="${COMPRESSION:-gzip}"
# Logging function
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $*" | tee -a "$BACKUP_DIR/backup.log"
}
# Check dependencies
check_dependencies() {
local missing_deps=()
if ! command -v pg_dump >/dev/null 2>&1; then
missing_deps+=("postgresql-client")
fi
if [ "$COMPRESSION" = "gzip" ] && ! command -v gzip >/dev/null 2>&1; then
missing_deps+=("gzip")
fi
if [ ${#missing_deps[@]} -gt 0 ]; then
log "ERROR: Missing dependencies: ${missing_deps[*]}"
exit 1
fi
}
# Create backup directory
setup_backup_dir() {
if [ ! -d "$BACKUP_DIR" ]; then
mkdir -p "$BACKUP_DIR"
log "INFO: Created backup directory: $BACKUP_DIR"
fi
}
# Perform database backup
backup_database() {
local timestamp=$(date +%Y%m%d_%H%M%S)
local backup_file="$BACKUP_DIR/${DB_NAME}_${timestamp}.sql"
log "INFO: Starting backup of database '$DB_NAME'"
# Set password if provided
if [ -n "$DB_PASSWORD" ]; then
export PGPASSWORD="$DB_PASSWORD"
fi
# Perform backup
if pg_dump -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" > "$backup_file"; then
log "INFO: Database backup completed: $backup_file"
# Compress if requested
if [ "$COMPRESSION" = "gzip" ]; then
if gzip "$backup_file"; then
backup_file="${backup_file}.gz"
log "INFO: Backup compressed: $backup_file"
else
log "WARN: Compression failed"
fi
fi
# Calculate file size
local file_size=$(stat -c%s "$backup_file" 2>/dev/null || stat -f%z "$backup_file" 2>/dev/null)
log "INFO: Backup size: $(numfmt --to=iec-i --suffix=B $file_size)"
echo "$backup_file"
else
log "ERROR: Database backup failed"
return 1
fi
}
# Clean old backups
cleanup_old_backups() {
log "INFO: Cleaning up backups older than $RETENTION_DAYS days"
local deleted_count=0
while IFS= read -r -d '' file; do
rm "$file"
log "INFO: Deleted old backup: $(basename "$file")"
((deleted_count++))
done < <(find "$BACKUP_DIR" -name "${DB_NAME}_*.sql*" -mtime +$RETENTION_DAYS -print0)
log "INFO: Deleted $deleted_count old backup(s)"
}
# Verify backup integrity
verify_backup() {
local backup_file="$1"
if [ ! -f "$backup_file" ]; then
log "ERROR: Backup file not found: $backup_file"
return 1
fi
# Check if file is compressed
if [[ "$backup_file" == *.gz ]]; then
if gzip -t "$backup_file"; then
log "INFO: Backup file integrity verified (compressed)"
return 0
else
log "ERROR: Backup file is corrupted (compressed)"
return 1
fi
else
# Check SQL file for basic structure
if grep -q "PostgreSQL database dump" "$backup_file"; then
log "INFO: Backup file integrity verified"
return 0
else
log "ERROR: Backup file appears to be corrupted"
return 1
fi
fi
}
# Send notification (email or webhook)
send_notification() {
local status="$1"
local message="$2"
if [ -n "$NOTIFICATION_EMAIL" ] && command -v mail >/dev/null 2>&1; then
echo "$message" | mail -s "Database Backup $status" "$NOTIFICATION_EMAIL"
log "INFO: Notification sent to $NOTIFICATION_EMAIL"
fi
if [ -n "$WEBHOOK_URL" ] && command -v curl >/dev/null 2>&1; then
curl -X POST -H "Content-Type: application/json" \
-d "{\"text\":\"Database Backup $status: $message\"}" \
"$WEBHOOK_URL" >/dev/null 2>&1
log "INFO: Webhook notification sent"
fi
}
# Main backup process
main() {
log "INFO: Starting database backup process"
check_dependencies
setup_backup_dir
if backup_file=$(backup_database); then
if verify_backup "$backup_file"; then
cleanup_old_backups
send_notification "SUCCESS" "Database backup completed successfully: $(basename "$backup_file")"
log "INFO: Backup process completed successfully"
else
send_notification "FAILED" "Backup verification failed"
log "ERROR: Backup verification failed"
exit 1
fi
else
send_notification "FAILED" "Database backup failed"
log "ERROR: Backup process failed"
exit 1
fi
}
# Handle command line arguments
case "${1:-backup}" in
backup) main ;;
verify) verify_backup "$2" ;;
cleanup) cleanup_old_backups ;;
*)
echo "Usage: $0 {backup|verify|cleanup}"
echo "Environment variables:"
echo " DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME"
echo " BACKUP_DIR, RETENTION_DAYS, COMPRESSION"
echo " NOTIFICATION_EMAIL, WEBHOOK_URL"
exit 1
;;
esacFile Processing Examples
Log Analyzer
#!/bin/bash
# log_analyzer.sh - Analyze web server logs
LOG_FILE="${1:-/var/log/apache2/access.log}"
OUTPUT_DIR="${2:-./log_analysis}"
# Check if log file exists
if [ ! -f "$LOG_FILE" ]; then
echo "Error: Log file '$LOG_FILE' not found"
exit 1
fi
# Create output directory
mkdir -p "$OUTPUT_DIR"
echo "Analyzing log file: $LOG_FILE"
echo "Output directory: $OUTPUT_DIR"
echo "=================================="
# Basic statistics
echo "Generating basic statistics..."
total_requests=$(wc -l < "$LOG_FILE")
echo "Total requests: $total_requests" > "$OUTPUT_DIR/summary.txt"
# Unique IP addresses
unique_ips=$(awk '{print $1}' "$LOG_FILE" | sort -u | wc -l)
echo "Unique IP addresses: $unique_ips" >> "$OUTPUT_DIR/summary.txt"
# Date range
first_date=$(head -1 "$LOG_FILE" | awk '{print $4}' | tr -d '[')
last_date=$(tail -1 "$LOG_FILE" | awk '{print $4}' | tr -d '[')
echo "Date range: $first_date to $last_date" >> "$OUTPUT_DIR/summary.txt"
# Top 10 IP addresses
echo "Generating top IP addresses..."
awk '{print $1}' "$LOG_FILE" | sort | uniq -c | sort -nr | head -10 > "$OUTPUT_DIR/top_ips.txt"
# Top 10 requested pages
echo "Generating top requested pages..."
awk '{print $7}' "$LOG_FILE" | sort | uniq -c | sort -nr | head -10 > "$OUTPUT_DIR/top_pages.txt"
# HTTP status codes
echo "Analyzing HTTP status codes..."
awk '{print $9}' "$LOG_FILE" | sort | uniq -c | sort -nr > "$OUTPUT_DIR/status_codes.txt"
# User agents
echo "Analyzing user agents..."
awk -F'"' '{print $6}' "$LOG_FILE" | sort | uniq -c | sort -nr | head -20 > "$OUTPUT_DIR/user_agents.txt"
# Hourly traffic distribution
echo "Generating hourly traffic distribution..."
awk '{print $4}' "$LOG_FILE" | cut -d: -f2 | sort | uniq -c > "$OUTPUT_DIR/hourly_traffic.txt"
# Error analysis (4xx and 5xx)
echo "Analyzing errors..."
awk '$9 ~ /^[45]/ {print $9, $7}' "$LOG_FILE" | sort | uniq -c | sort -nr > "$OUTPUT_DIR/errors.txt"
# Bandwidth usage (if log format includes bytes)
echo "Calculating bandwidth usage..."
if awk '{print $10}' "$LOG_FILE" | head -1 | grep -q '^[0-9]*$'; then
total_bytes=$(awk '{sum += $10} END {print sum}' "$LOG_FILE")
echo "Total bandwidth: $(numfmt --to=iec-i --suffix=B $total_bytes)" >> "$OUTPUT_DIR/summary.txt"
fi
# Generate HTML report
echo "Generating HTML report..."
cat > "$OUTPUT_DIR/report.html" << EOF
<!DOCTYPE html>
<html>
<head>
<title>Log Analysis Report</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.section { margin: 20px 0; }
.data { background: #f5f5f5; padding: 10px; border-radius: 5px; }
pre { white-space: pre-wrap; }
</style>
</head>
<body>
<h1>Log Analysis Report</h1>
<p>Generated on: $(date)</p>
<div class="section">
<h2>Summary</h2>
<div class="data">
<pre>$(cat "$OUTPUT_DIR/summary.txt")</pre>
</div>
</div>
<div class="section">
<h2>Top 10 IP Addresses</h2>
<div class="data">
<pre>$(cat "$OUTPUT_DIR/top_ips.txt")</pre>
</div>
</div>
<div class="section">
<h2>Top 10 Requested Pages</h2>
<div class="data">
<pre>$(cat "$OUTPUT_DIR/top_pages.txt")</pre>
</div>
</div>
<div class="section">
<h2>HTTP Status Codes</h2>
<div class="data">
<pre>$(cat "$OUTPUT_DIR/status_codes.txt")</pre>
</div>
</div>
</body>
</html>
EOF
echo "Analysis complete! Results saved in: $OUTPUT_DIR"
echo "View the HTML report: $OUTPUT_DIR/report.html"File Organizer
#!/bin/bash
# file_organizer.sh - Organize files by type and date
SOURCE_DIR="${1:-.}"
TARGET_DIR="${2:-./organized}"
DRY_RUN="${DRY_RUN:-false}"
# File type associations
declare -A file_types=(
["jpg,jpeg,png,gif,bmp,tiff"]="Images"
["mp4,avi,mkv,mov,wmv,flv"]="Videos"
["mp3,wav,flac,aac,ogg"]="Audio"
["pdf,doc,docx,txt,rtf,odt"]="Documents"
["zip,rar,7z,tar,gz,bz2"]="Archives"
["exe,msi,deb,rpm,dmg"]="Executables"
["html,css,js,php,py,java,cpp,c"]="Code"
)
# Logging function
log() {
echo "$(date '+%H:%M:%S') - $*"
}
# Get file category based on extension
get_file_category() {
local filename="$1"
local extension="${filename##*.}"
extension=$(echo "$extension" | tr '[:upper:]' '[:lower:]')
for types in "${!file_types[@]}"; do
if [[ ",$types," == *",$extension,"* ]]; then
echo "${file_types[$types]}"
return
fi
done
echo "Other"
}
# Create directory structure
create_directory() {
local dir="$1"
if [ "$DRY_RUN" = "true" ]; then
log "DRY RUN: Would create directory: $dir"
else
mkdir -p "$dir"
log "Created directory: $dir"
fi
}
# Move file
move_file() {
local source="$1"
local target="$2"
if [ "$DRY_RUN" = "true" ]; then
log "DRY RUN: Would move '$source' to '$target'"
else
if mv "$source" "$target"; then
log "Moved: $(basename "$source") -> $target"
else
log "ERROR: Failed to move $(basename "$source")"
fi
fi
}
# Organize files
organize_files() {
local source_dir="$1"
local target_dir="$2"
local file_count=0
local dir_count=0
log "Starting file organization..."
log "Source: $source_dir"
log "Target: $target_dir"
# Process all files in source directory
find "$source_dir" -maxdepth 1 -type f | while read -r file; do
# Skip hidden files unless specified
if [[ "$(basename "$file")" == .* ]] && [ "$INCLUDE_HIDDEN" != "true" ]; then
continue
fi
# Get file info
filename=$(basename "$file")
category=$(get_file_category "$filename")
# Get file date
if command -v stat >/dev/null 2>&1; then
file_date=$(stat -c %Y "$file" 2>/dev/null || stat -f %m "$file" 2>/dev/null)
year=$(date -d "@$file_date" +%Y 2>/dev/null || date -r "$file_date" +%Y 2>/dev/null)
month=$(date -d "@$file_date" +%m 2>/dev/null || date -r "$file_date" +%m 2>/dev/null)
else
year=$(date +%Y)
month=$(date +%m)
fi
# Create target directory structure
target_path="$target_dir/$category/$year/$month"
create_directory "$target_path"
# Move file
move_file "$file" "$target_path/$filename"
((file_count++))
done
log "Organization complete. Processed $file_count files."
}
# Generate report
generate_report() {
local target_dir="$1"
local report_file="$target_dir/organization_report.txt"
log "Generating organization report..."
{
echo "File Organization Report"
echo "======================="
echo "Generated on: $(date)"
echo "Target directory: $target_dir"
echo
echo "Directory Structure:"
echo "==================="
if [ -d "$target_dir" ]; then
find "$target_dir" -type d | sort
fi
echo
echo "File Count by Category:"
echo "======================"
for category in "${file_types[@]}" "Other"; do
if [ -d "$target_dir/$category" ]; then
count=$(find "$target_dir/$category" -type f | wc -l)
printf "%-15s: %d files\n" "$category" "$count"
fi
done
} > "$report_file"
log "Report saved: $report_file"
}
# Show usage
show_usage() {
cat << EOF
Usage: $0 [source_dir] [target_dir]
Options:
source_dir Source directory to organize (default: current directory)
target_dir Target directory for organized files (default: ./organized)
Environment Variables:
DRY_RUN=true Show what would be done without actually moving files
INCLUDE_HIDDEN=true Include hidden files in organization
Examples:
$0 # Organize current directory
$0 /home/user/Downloads # Organize Downloads folder
DRY_RUN=true $0 # Preview organization without moving files
File Categories:
EOF
for types in "${!file_types[@]}"; do
printf " %-15s: %s\n" "${file_types[$types]}" "$types"
done
}
# Main execution
main() {
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
show_usage
exit 0
fi
if [ ! -d "$SOURCE_DIR" ]; then
log "ERROR: Source directory '$SOURCE_DIR' does not exist"
exit 1
fi
if [ "$DRY_RUN" = "true" ]; then
log "DRY RUN MODE - No files will be moved"
fi
organize_files "$SOURCE_DIR" "$TARGET_DIR"
if [ "$DRY_RUN" != "true" ]; then
generate_report "$TARGET_DIR"
fi
}
main "$@"CSV Processor
#!/bin/bash
# csv_processor.sh - Process and analyze CSV files
CSV_FILE="$1"
DELIMITER="${DELIMITER:-,}"
OUTPUT_FORMAT="${OUTPUT_FORMAT:-table}"
# Check if file exists
if [ ! -f "$CSV_FILE" ]; then
echo "Usage: $0 <csv_file>"
echo "Environment variables:"
echo " DELIMITER - Field delimiter (default: ,)"
echo " OUTPUT_FORMAT - Output format: table, json, html (default: table)"
exit 1
fi
# Function to get CSV headers
get_headers() {
head -1 "$CSV_FILE" | tr "$DELIMITER" '\n' | nl -w2 -s': '
}
# Function to count rows
count_rows() {
local total_rows=$(wc -l < "$CSV_FILE")
local data_rows=$((total_rows - 1))
echo "Total rows: $total_rows (including header)"
echo "Data rows: $data_rows"
}
# Function to show column statistics
column_stats() {
local column_num="$1"
local column_name=$(head -1 "$CSV_FILE" | cut -d"$DELIMITER" -f"$column_num")
echo "Statistics for column $column_num: $column_name"
echo "============================================="
# Get column data (skip header)
tail -n +2 "$CSV_FILE" | cut -d"$DELIMITER" -f"$column_num" > /tmp/column_data.tmp
# Count non-empty values
local non_empty=$(grep -v '^$' /tmp/column_data.tmp | wc -l)
local empty=$(grep '^$' /tmp/column_data.tmp | wc -l)
echo "Non-empty values: $non_empty"
echo "Empty values: $empty"
# Check if column contains numbers
if grep -q '^[0-9]*\.?[0-9]*$' /tmp/column_data.tmp; then
echo "Data type: Numeric"
# Calculate numeric statistics
local sum=$(awk '{sum += $1} END {print sum}' /tmp/column_data.tmp)
local count=$(grep -v '^$' /tmp/column_data.tmp | wc -l)
local avg=$(echo "scale=2; $sum / $count" | bc -l 2>/dev/null || echo "N/A")
local min=$(sort -n /tmp/column_data.tmp | head -1)
local max=$(sort -n /tmp/column_data.tmp | tail -1)
echo "Sum: $sum"
echo "Average: $avg"
echo "Min: $min"
echo "Max: $max"
else
echo "Data type: Text"
# Show unique values count
local unique_count=$(sort /tmp/column_data.tmp | uniq | wc -l)
echo "Unique values: $unique_count"
# Show most common values
echo "Most common values:"
sort /tmp/column_data.tmp | uniq -c | sort -nr | head -5
fi
rm -f /tmp/column_data.tmp
}
# Function to filter CSV data
filter_data() {
local column="$1"
local operator="$2"
local value="$3"
echo "Filtering: Column $column $operator '$value'"
echo "==========================================="
# Show header
head -1 "$CSV_FILE"
# Apply filter based on operator
case "$operator" in
"="|"eq")
tail -n +2 "$CSV_FILE" | awk -F"$DELIMITER" -v col="$column" -v val="$value" '$col == val'
;;
"!="|"ne")
tail -n +2 "$CSV_FILE" | awk -F"$DELIMITER" -v col="$column" -v val="$value" '$col != val'
;;
">"|"gt")
tail -n +2 "$CSV_FILE" | awk -F"$DELIMITER" -v col="$column" -v val="$value" '$col > val'
;;
"<"|"lt")
tail -n +2 "$CSV_FILE" | awk -F"$DELIMITER" -v col="$column" -v val="$value" '$col < val'
;;
"contains")
tail -n +2 "$CSV_FILE" | awk -F"$DELIMITER" -v col="$column" -v val="$value" 'index($col, val) > 0'
;;
*)
echo "Supported operators: =, !=, >, <, contains"
return 1
;;
esac
}
# Function to convert to JSON
to_json() {
local headers=($(head -1 "$CSV_FILE" | tr "$DELIMITER" ' '))
echo "["
local first_row=true
tail -n +2 "$CSV_FILE" | while IFS="$DELIMITER" read -r -a fields; do
if [ "$first_row" = true ]; then
first_row=false
else
echo -n ","
fi
echo -n " {"
for i in "${!headers[@]}"; do
if [ $i -gt 0 ]; then
echo -n ", "
fi
echo -n "\"${headers[i]}\": \"${fields[i]}\""
done
echo -n "}"
done
echo
echo "]"
}
# Function to convert to HTML table
to_html() {
echo "<table border='1'>"
echo "<thead><tr>"
# Headers
head -1 "$CSV_FILE" | tr "$DELIMITER" '\n' | while read -r header; do
echo "<th>$header</th>"
done
echo "</tr></thead><tbody>"
# Data rows
tail -n +2 "$CSV_FILE" | while IFS="$DELIMITER" read -r -a fields; do
echo "<tr>"
for field in "${fields[@]}"; do
echo "<td>$field</td>"
done
echo "</tr>"
done
echo "</tbody></table>"
}
# Function to show menu
show_menu() {
echo
echo "CSV Processor Menu"
echo "=================="
echo "1. Show headers"
echo "2. Count rows"
echo "3. Column statistics"
echo "4. Filter data"
echo "5. Convert to JSON"
echo "6. Convert to HTML"
echo "7. Show first 10 rows"
echo "8. Show last 10 rows"
echo "9. Exit"
echo
}
# Interactive mode
interactive_mode() {
while true; do
show_menu
read -p "Choose option: " choice
case $choice in
1)
echo "CSV Headers:"
get_headers
;;
2)
count_rows
;;
3)
echo "Available columns:"
get_headers
read -p "Enter column number: " col_num
column_stats "$col_num"
;;
4)
echo "Available columns:"
get_headers
read -p "Enter column number: " col_num
read -p "Enter operator (=, !=, >, <, contains): " operator
read -p "Enter value: " value
filter_data "$col_num" "$operator" "$value"
;;
5)
to_json
;;
6)
to_html
;;
7)
echo "First 10 rows:"
head -11 "$CSV_FILE" | column -t -s"$DELIMITER"
;;
8)
echo "Last 10 rows:"
tail -10 "$CSV_FILE" | column -t -s"$DELIMITER"
;;
9)
echo "Goodbye!"
exit 0
;;
*)
echo "Invalid choice"
;;
esac
echo
read -p "Press Enter to continue..."
done
}
# Main execution
echo "Processing CSV file: $CSV_FILE"
echo "Delimiter: '$DELIMITER'"
echo
# If no additional arguments, start interactive mode
if [ $# -eq 1 ]; then
interactive_mode
else
# Command line mode
case "$2" in
headers) get_headers ;;
count) count_rows ;;
stats) column_stats "$3" ;;
filter) filter_data "$3" "$4" "$5" ;;
json) to_json ;;
html) to_html ;;
*)
echo "Usage: $0 <csv_file> [command] [args...]"
echo "Commands: headers, count, stats <col>, filter <col> <op> <val>, json, html"
;;
esac
fiSystem Administration Examples
System Monitor
#!/bin/bash
# system_monitor.sh - Comprehensive system monitoring script
ALERT_CPU_THRESHOLD=${ALERT_CPU_THRESHOLD:-80}
ALERT_MEMORY_THRESHOLD=${ALERT_MEMORY_THRESHOLD:-85}
ALERT_DISK_THRESHOLD=${ALERT_DISK_THRESHOLD:-90}
LOG_FILE=${LOG_FILE:-"/var/log/system_monitor.log"}
EMAIL_ALERT=${EMAIL_ALERT:-""}
WEBHOOK_URL=${WEBHOOK_URL:-""}
# Colors for output
RED='\033[0;31m'
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color
# Logging function
log_message() {
local level="$1"
shift
local message="$*"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] [$level] $message" >> "$LOG_FILE"
case "$level" in
ERROR) echo -e "${RED}[$timestamp] ERROR: $message${NC}" ;;
WARN) echo -e "${YELLOW}[$timestamp] WARN: $message${NC}" ;;
INFO) echo -e "${GREEN}[$timestamp] INFO: $message${NC}" ;;
esac
}
# Send alert notification
send_alert() {
local subject="$1"
local message="$2"
# Email notification
if [ -n "$EMAIL_ALERT" ] && command -v mail >/dev/null 2>&1; then
echo "$message" | mail -s "$subject" "$EMAIL_ALERT"
log_message INFO "Alert sent via email to $EMAIL_ALERT"
fi
# Webhook notification
if [ -n "$WEBHOOK_URL" ] && command -v curl >/dev/null 2>&1; then
curl -X POST -H "Content-Type: application/json" \
-d "{\"text\":\"$subject: $message\"}" \
"$WEBHOOK_URL" >/dev/null 2>&1
log_message INFO "Alert sent via webhook"
fi
}
# Check CPU usage
check_cpu() {
local cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
cpu_usage=${cpu_usage%.*} # Remove decimal part
echo "CPU Usage: ${cpu_usage}%"
if [ "$cpu_usage" -gt "$ALERT_CPU_THRESHOLD" ]; then
local message="High CPU usage detected: ${cpu_usage}% (threshold: ${ALERT_CPU_THRESHOLD}%)"
log_message WARN "$message"
send_alert "CPU Alert" "$message"
# Show top processes
echo "Top CPU consuming processes:"
ps aux --sort=-%cpu | head -6
else
log_message INFO "CPU usage normal: ${cpu_usage}%"
fi
}
# Check memory usage
check_memory() {
local memory_info=$(free | grep Mem)
local total=$(echo $memory_info | awk '{print $2}')
local used=$(echo $memory_info | awk '{print $3}')
local memory_usage=$((used * 100 / total))
echo "Memory Usage: ${memory_usage}% ($(numfmt --to=iec $((used * 1024))) / $(numfmt --to=iec $((total * 1024))))"
if [ "$memory_usage" -gt "$ALERT_MEMORY_THRESHOLD" ]; then
local message="High memory usage detected: ${memory_usage}% (threshold: ${ALERT_MEMORY_THRESHOLD}%)"
log_message WARN "$message"
send_alert "Memory Alert" "$message"
# Show top memory consuming processes
echo "Top memory consuming processes:"
ps aux --sort=-%mem | head -6
else
log_message INFO "Memory usage normal: ${memory_usage}%"
fi
}
# Check disk usage
check_disk() {
echo "Disk Usage:"
df -h | grep -vE '^Filesystem|tmpfs|cdrom' | while read output; do
usage=$(echo $output | awk '{print $5}' | cut -d'%' -f1)
partition=$(echo $output | awk '{print $1}')
mount_point=$(echo $output | awk '{print $6}')
echo " $mount_point: ${usage}%"
if [ "$usage" -gt "$ALERT_DISK_THRESHOLD" ]; then
local message="High disk usage detected on $mount_point: ${usage}% (threshold: ${ALERT_DISK_THRESHOLD}%)"
log_message WARN "$message"
send_alert "Disk Alert" "$message"
else
log_message INFO "Disk usage normal on $mount_point: ${usage}%"
fi
done
}
# Check system load
check_load() {
local load_avg=$(uptime | awk -F'load average:' '{print $2}')
local load_1min=$(echo $load_avg | awk '{print $1}' | tr -d ',')
local cpu_cores=$(nproc)
echo "Load Average: $load_avg"
echo "CPU Cores: $cpu_cores"
# Check if 1-minute load is higher than number of cores
if (( $(echo "$load_1min > $cpu_cores" | bc -l) )); then
local message="High system load detected: $load_1min (cores: $cpu_cores)"
log_message WARN "$message"
send_alert "Load Alert" "$message"
else
log_message INFO "System load normal: $load_1min"
fi
}
# Check running services
check_services() {
local services=("ssh" "nginx" "apache2" "mysql" "postgresql")
echo "Service Status:"
for service in "${services[@]}"; do
if systemctl is-active --quiet "$service" 2>/dev/null; then
echo " $service: Running"
log_message INFO "Service $service is running"
elif systemctl list-unit-files | grep -q "^$service.service"; then
echo " $service: Stopped"
log_message WARN "Service $service is stopped"
send_alert "Service Alert" "Service $service is not running"
fi
done
}
# Check network connectivity
check_network() {
local test_hosts=("8.8.8.8" "1.1.1.1" "google.com")
echo "Network Connectivity:"
for host in "${test_hosts[@]}"; do
if ping -c 1 -W 5 "$host" >/dev/null 2>&1; then
echo " $host: OK"
log_message INFO "Network connectivity to $host OK"
else
echo " $host: FAILED"
log_message ERROR "Network connectivity to $host failed"
send_alert "Network Alert" "Cannot reach $host"
fi
done
}
# Check log files for errors
check_logs() {
local log_files=("/var/log/syslog" "/var/log/auth.log" "/var/log/kern.log")
local time_threshold="1 hour ago"
echo "Recent Log Errors:"
for log_file in "${log_files[@]}"; do
if [ -f "$log_file" ]; then
local error_count=$(find "$log_file" -newermt "$time_threshold" -exec grep -i "error\|critical\|fatal" {} \; 2>/dev/null | wc -l)
echo " $(basename "$log_file"): $error_count errors in last hour"
if [ "$error_count" -gt 10 ]; then
log_message WARN "High error count in $log_file: $error_count"
send_alert "Log Alert" "High error count in $log_file: $error_count errors in last hour"
fi
fi
done
}
# Generate system report
generate_report() {
local report_file="/tmp/system_report_$(date +%Y%m%d_%H%M%S).txt"
{
echo "System Monitoring Report"
echo "======================="
echo "Generated: $(date)"
echo "Hostname: $(hostname)"
echo
echo "System Information:"
echo "=================="
uname -a
echo
echo "Uptime:"
echo "======="
uptime
echo
echo "CPU Information:"
echo "==============="
lscpu | grep -E "Model name|CPU\(s\)|Thread|Core"
echo
echo "Memory Information:"
echo "=================="
free -h
echo
echo "Disk Usage:"
echo "==========="
df -h
echo
echo "Network Interfaces:"
echo "=================="
ip addr show
echo
echo "Running Processes (Top 10 by CPU):"
echo "=================================="
ps aux --sort=-%cpu | head -11
echo
echo "Running Processes (Top 10 by Memory):"
echo "====================================="
ps aux --sort=-%mem | head -11
} > "$report_file"
echo "System report generated: $report_file"
log_message INFO "System report generated: $report_file"
}
# Main monitoring function
run_monitoring() {
log_message INFO "Starting system monitoring"
echo "System Monitoring Report - $(date)"
echo "=================================="
echo
check_cpu
echo
check_memory
echo
check_disk
echo
check_load
echo
check_services
echo
check_network
echo
check_logs
echo
log_message INFO "System monitoring completed"
}
# Show usage
show_usage() {
cat << EOF
Usage: $0 [options]
Options:
-c, --continuous Run monitoring continuously
-i, --interval N Set monitoring interval in seconds (default: 300)
-r, --report Generate detailed system report
-h, --help Show this help message
Environment Variables:
ALERT_CPU_THRESHOLD CPU usage alert threshold (default: 80)
ALERT_MEMORY_THRESHOLD Memory usage alert threshold (default: 85)
ALERT_DISK_THRESHOLD Disk usage alert threshold (default: 90)
LOG_FILE Log file path (default: /var/log/system_monitor.log)
EMAIL_ALERT Email address for alerts
WEBHOOK_URL Webhook URL for alerts
Examples:
$0 # Run monitoring once
$0 -c # Run continuous monitoring
$0 -c -i 60 # Run continuous monitoring every 60 seconds
$0 -r # Generate detailed report
EOF
}
# Parse command line arguments
CONTINUOUS=false
INTERVAL=300
while [[ $# -gt 0 ]]; do
case $1 in
-c|--continuous)
CONTINUOUS=true
shift
;;
-i|--interval)
INTERVAL="$2"
shift 2
;;
-r|--report)
generate_report
exit 0
;;
-h|--help)
show_usage
exit 0
;;
*)
echo "Unknown option: $1"
show_usage
exit 1
;;
esac
done
# Main execution
if [ "$CONTINUOUS" = true ]; then
log_message INFO "Starting continuous monitoring (interval: ${INTERVAL}s)"
while true; do
run_monitoring
echo "Sleeping for $INTERVAL seconds..."
sleep "$INTERVAL"
done
else
run_monitoring
fiThis workbook provides a comprehensive collection of Bash scripting examples ranging from basic to intermediate level. Each example includes practical, real-world scenarios that demonstrate different aspects of Bash programming. The examples cover:
- Basic Examples: Variables, user input, calculations, file operations
- Intermediate Examples: Functions, configuration management, database operations
- File Processing: Log analysis, file organization, CSV processing
- System Administration: Comprehensive system monitoring
These examples can be used as learning materials, reference implementations, or starting points for your own scripts.