Practical shell scripting projects for OS labs — system monitoring scripts, automated backup, log analyzer, process manager, and user administration tools with complete Bash implementations.
Introduction
Shell scripting is where operating system theory meets daily practice. System administrators, DevOps engineers, and developers use shell scripts to automate repetitive tasks, monitor systems, and manage infrastructure. These projects will teach you to write practical scripts that solve real problems — exactly what interviewers and employers look for.
Each project below includes the complete implementation, explanation of key concepts, and suggestions for extending the functionality.
Project 1: System Health Monitor
This script checks CPU usage, memory, disk space, and running processes, alerting when thresholds are exceeded.
#!/bin/bash
# system_monitor.sh — Monitor system health and alert on issues
THRESHOLD_CPU=80
THRESHOLD_MEM=90
THRESHOLD_DISK=85
LOG_FILE="/var/log/system_health.log"
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') — $1" >> "$LOG_FILE"
echo "$1"
}
# Check CPU usage (average over 5 seconds)
check_cpu() {
cpu_usage=$(top -bn2 -d1 | grep "Cpu(s)" | tail -1 | awk '{print $2}' | cut -d. -f1)
if [ "$cpu_usage" -gt "$THRESHOLD_CPU" ]; then
log_message "WARNING: CPU usage at ${cpu_usage}% (threshold: ${THRESHOLD_CPU}%)"
echo "Top CPU consumers:"
ps aux --sort=-%cpu | head -5
else
log_message "OK: CPU usage at ${cpu_usage}%"
fi
}
# Check memory usage
check_memory() {
mem_usage=$(free | awk '/Mem:/ {printf "%.0f", $3/$2 * 100}')
if [ "$mem_usage" -gt "$THRESHOLD_MEM" ]; then
log_message "WARNING: Memory usage at ${mem_usage}% (threshold: ${THRESHOLD_MEM}%)"
echo "Top memory consumers:"
ps aux --sort=-%mem | head -5
else
log_message "OK: Memory usage at ${mem_usage}%"
fi
}
# Check disk usage
check_disk() {
while read -r line; do
usage=$(echo "$line" | awk '{print $5}' | tr -d '%')
mount=$(echo "$line" | awk '{print $6}')
if [ "$usage" -gt "$THRESHOLD_DISK" ]; then
log_message "WARNING: Disk $mount at ${usage}% (threshold: ${THRESHOLD_DISK}%)"
fi
done < <(df -h | grep '^/dev/')
log_message "OK: Disk check complete"
}
# Run all checks
echo "=== System Health Report — $(date) ==="
check_cpu
check_memory
check_disk
echo "=== Report Complete ==="
Key Concepts: Command substitution $(...), arithmetic comparison, output redirection, piping between commands, awk for text parsing.
Project 2: Automated Backup Script
Creates incremental backups with rotation, compression, and email notification.
#!/bin/bash
# backup.sh — Automated backup with rotation
SOURCE_DIR="/home/user/documents"
BACKUP_DIR="/backup"
MAX_BACKUPS=7
DATE=$(date '+%Y%m%d_%H%M%S')
BACKUP_FILE="$BACKUP_DIR/backup_$DATE.tar.gz"
# Create backup directory if missing
mkdir -p "$BACKUP_DIR"
# Create compressed archive
echo "Starting backup of $SOURCE_DIR..."
tar -czf "$BACKUP_FILE" "$SOURCE_DIR" 2>/dev/null
if [ $? -eq 0 ]; then
SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
echo "Backup successful: $BACKUP_FILE ($SIZE)"
# Rotate old backups — keep only MAX_BACKUPS most recent
backup_count=$(ls -1 "$BACKUP_DIR"/backup_*.tar.gz 2>/dev/null | wc -l)
if [ "$backup_count" -gt "$MAX_BACKUPS" ]; then
remove_count=$((backup_count - MAX_BACKUPS))
ls -1t "$BACKUP_DIR"/backup_*.tar.gz | tail -"$remove_count" | while read -r old; do
echo "Removing old backup: $old"
rm -f "$old"
done
fi
else
echo "ERROR: Backup failed!"
exit 1
fi
Key Concepts: Exit status checking ($?), date formatting, tar compression, file rotation logic, here-string arithmetic.
Project 3: Log File Analyzer
Parses web server or system logs to extract statistics, detect errors, and identify patterns.
#!/bin/bash
# log_analyzer.sh — Analyze Apache/Nginx access logs
LOG_FILE="${1:-/var/log/apache2/access.log}"
if [ ! -f "$LOG_FILE" ]; then
echo "Error: Log file $LOG_FILE not found"
exit 1
fi
echo "=== Log Analysis Report for $LOG_FILE ==="
echo "Total requests: $(wc -l < "$LOG_FILE")"
echo ""
echo "--- Top 10 IP Addresses ---"
awk '{print $1}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -10
echo ""
echo "--- HTTP Status Code Distribution ---"
awk '{print $9}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -10
echo ""
echo "--- Top 10 Requested URLs ---"
awk '{print $7}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -10
echo ""
echo "--- Requests Per Hour ---"
awk -F'[' '{print $2}' "$LOG_FILE" | awk -F: '{print $2}' | sort | uniq -c | sort -k2n
echo ""
echo "--- 404 Errors ---"
awk '$9 == 404 {print $7}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -10
echo ""
echo "--- Potential Security Threats (suspicious patterns) ---"
grep -iE "(union.*select|<script|/etc/passwd|\.\.\/)" "$LOG_FILE" | tail -5
Key Concepts: awk field extraction, sort/uniq pipeline for frequency counting, grep with regex for pattern matching, default parameter values.
Project 4: Process Manager
A menu-driven interactive script for managing processes — listing, searching, killing, and monitoring.
#!/bin/bash
# process_manager.sh — Interactive process management tool
show_menu() {
echo ""
echo "=== Process Manager ==="
echo "1. List all processes (sorted by CPU)"
echo "2. Search process by name"
echo "3. Kill a process by PID"
echo "4. Show process tree"
echo "5. Monitor a specific process"
echo "6. Find zombie processes"
echo "7. Show system summary"
echo "0. Exit"
echo -n "Choice: "
}
while true; do
show_menu
read -r choice
case $choice in
1) ps aux --sort=-%cpu | head -20 ;;
2) echo -n "Process name: "; read -r name
ps aux | grep -i "$name" | grep -v grep ;;
3) echo -n "Enter PID to kill: "; read -r pid
if kill -0 "$pid" 2>/dev/null; then
echo -n "Send SIGTERM (t) or SIGKILL (k)? "
read -r sig
[ "$sig" = "k" ] && kill -9 "$pid" || kill "$pid"
echo "Signal sent to PID $pid"
else
echo "Process $pid does not exist"
fi ;;
4) pstree -p | head -40 ;;
5) echo -n "Enter PID to monitor: "; read -r pid
echo "Monitoring PID $pid (Ctrl+C to stop)..."
while kill -0 "$pid" 2>/dev/null; do
ps -p "$pid" -o pid,ppid,%cpu,%mem,etime,cmd
sleep 2
done
echo "Process $pid has terminated." ;;
6) echo "Zombie processes:"
ps aux | awk '$8=="Z" || $8=="Z+"' ;;
7) echo "Uptime:"; uptime
echo ""; echo "Memory:"; free -h
echo ""; echo "Load:"; cat /proc/loadavg ;;
0) echo "Goodbye!"; exit 0 ;;
*) echo "Invalid choice" ;;
esac
done
Key Concepts: Case statements, while loops, signal handling, process verification with kill -0, interactive input.
Project 5: User Account Manager
Automates user creation, deletion, and permission management.
#!/bin/bash
# user_manager.sh — Batch user account management
create_users_from_file() {
local file="$1"
while IFS=',' read -r username fullname group; do
if id "$username" &>/dev/null; then
echo "User $username already exists — skipping"
else
useradd -m -c "$fullname" -g "$group" "$username"
echo "$(openssl rand -base64 12)" | passwd --stdin "$username"
echo "Created user: $username ($fullname) in group $group"
fi
done < "$file"
}
# Usage: Create a CSV file with: username,fullname,group
# Then run: sudo ./user_manager.sh users.csv
if [ "$#" -eq 1 ] && [ -f "$1" ]; then
create_users_from_file "$1"
else
echo "Usage: $0 <users.csv>"
echo "CSV format: username,fullname,group"
fi
Best Practices Demonstrated
- Always quote variables —
"$variable" prevents word splitting - Check exit status —
if [ $? -eq 0 ] or if command; then - Use functions — organize code into reusable blocks
- Handle errors gracefully — validate inputs, provide helpful messages
- Add logging — redirect important events to log files
- Use shellcheck — run
shellcheck script.sh to find common bugs
Running These Projects
# Make script executable
chmod +x script.sh
# Run directly
./script.sh
# Run with bash explicitly (useful for debugging)
bash -x script.sh # prints each command before executing
# Schedule with cron (e.g., backup every day at 2 AM)
crontab -e
# Add: 0 2 * * * /path/to/backup.sh
Learning Outcomes
After completing these projects, you will be able to:
- Write production-quality Bash scripts with proper error handling
- Parse and analyze log files using text processing tools
- Automate system administration tasks
- Build interactive command-line tools
- Schedule automated tasks with cron