Welcome to Part 13 of the Linux Bash Course. In this section, we transition from theory to practice. You will build three robust, real-world utility projects that apply everything you have learned about loops, logic, filters, and system files.
Chapter 37: Project 1 – Interactive Menu Script
37.1 Project Goal and Requirements: User Interface Design in Terminal
The goal of this project is to create an interactive terminal dashboard. The script must display a clean visual text interface, accept user input to run system diagnostic commands, and loop indefinitely until the user selects “Exit”.
37.2 Defining Project Structure, Variables, and Help Menu
Start by setting up the file layout. We use a standard shebang, strict shell flags (-u to catch unset variables), and define clean color variables using ANSI escape codes for styling.
#!/bin/bash
set -u
# Define color constants
BLUE='\033[0;34m'
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m' # No Color
37.3 Prompting for User Input: Using read with timeouts
We use the read command to capture input. The -p option prints a custom prompt, and -r prevents backslash escapes from interpolating.
-
Syntax:
read [options] variable_name -
Example Command:
read -r -p "Enter your choice [1-4]: " choice -
Expected Output:
Enter your choice [1-4]: _ -
Flag & Command Breakdown:
read: Reads input from standard input.-r: Disables backslash escape sequences (raw input mode).-p: Displays a prompt string before waiting for input.choice: Variable where the user input is saved.
37.4 Implementing a robust case statement loop for menu options
To process choices, wrap a conditional case statement inside an infinite while true loop.
while true; do
# Display menu options
echo "1. Show System Details"
echo "2. List Logged-In Users"
echo "3. Exit"
read -r -p "Choice: " opt
case "$opt" in
1) show_system_info ;;
2) list_users ;;
3) break ;;
*) echo "Invalid option!" ;;
esac
done
37.5 Menu Option 1: Displaying System Details and Host Information
To display host details, we call the hostnamectl command and extract specific fields.
-
Syntax:
hostnamectl [options] -
Example Command:
hostnamectl status --static -
Expected Output:
ubuntu-server-22 -
Flag & Command Breakdown:
hostnamectl: Configures and queries the system hostname.status: Displays current hostname status information.--static: Outputs only the static system hostname.
37.6 Menu Option 2: Checking and listing logged-in users
We query the system database using who to show who is currently logged into the shell.
-
Syntax:
who [options] -
Example Command:
who -H -
Expected Output:
NAME LINE TIME COMMENT suresh tty1 2026-06-12 06:00 -
Flag & Command Breakdown:
who: Displays active user sessions.-H: Prints table column headings at the top of the output.
37.7 Menu Option 3: Showing live memory and CPU percentages
We read system memory details using free.
-
Syntax:
free [options] -
Example Command:
free -h -t -
Expected Output:
total used free shared buff/cache available Mem: 7.7Gi 2.1Gi 3.2Gi 215Mi 2.4Gi 5.2Gi Swap: 2.0Gi 100Mi 1.9Gi Total: 9.7Gi 2.2Gi 5.1Gi -
Flag & Command Breakdown:
free: Displays RAM and swap usage.-h: Human-readable format.-t: Displays a summary line containing totals.
37.8 Validating input arguments and handling empty or incorrect options
Always validate variables to prevent errors. Ensure input is not empty using a conditional expression:
if [[ -z "$choice" ]]; then
echo "Input cannot be empty. Please enter a valid number."
continue
fi
If the user inputs letters when numbers are expected, a fallback case * catches it and prompts the user to try again.
37.9 Adding colors and clear/reset layouts for terminal feedback
Use the clear utility to reset the terminal layout, creating a dashboard feel.
-
Syntax:
clear -
Example Command:
clear -
Expected Output: (The terminal screen is cleared, and the cursor returns to the top-left)
-
Flag & Command Breakdown:
clear: Clears the screen.
37.10 Full Code Assembly and Shell testing of the Menu Script
Create the file dashboard.sh, make it executable, and run it:
# Save to file, make executable, and run:
chmod +x dashboard.sh
./dashboard.sh
Here is the complete script:
#!/bin/bash
set -u
# Colors
BLUE='\033[0;34m'
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'
while true; do
echo -e "${BLUE}=== SYSTEM DASHBOARD ===${NC}"
echo "1. Hostname Info"
echo "2. Logged-in Users"
echo "3. Free Memory"
echo "4. Exit"
read -r -p "Select [1-4]: " opt
case "$opt" in
1) hostnamectl ;;
2) who -H ;;
3) free -h ;;
4) echo "Goodbye!"; exit 0 ;;
*) echo -e "${RED}Invalid Selection${NC}" ;;
esac
done
Chapter 38: Project 2 – System Health Monitor
38.1 Project Architecture: Monitoring system metrics via background script
This daemon runs in the background. It periodically checks resource usage and alerts the administrator if any metric exceeds pre-configured thresholds.
38.2 Setting Up threshold variables for CPU, Memory, and Disk Alerting
Establish baseline limits at the beginning of the script:
# Threshold limits (percent)
CPU_LIMIT=80
MEM_LIMIT=85
DISK_LIMIT=90
ALERT_LOG="/var/log/system_health.log"
38.3 Getting CPU usage using top and awk commands
Extract CPU idle time using top and calculate the used percentage.
-
Syntax:
top -b -n 1 -
Example Command:
top -b -n 1 | grep "Cpu(s)" | awk '{print $8}' -
Expected Output:
92.5 -
Flag & Command Breakdown:
top: Interactive system monitor.-b: Batch mode (runs once and exits, allowing command output redirection).-n 1: Runs for a single loop iteration.grep "Cpu(s)": Extracts the line containing CPU statistics.awk '{print $8}': Isolates the 8th field (percentage of idle CPU).
38.4 Extracting RAM usage percentages using free and awk
Filter output to calculate the current memory usage percentage.
-
Syntax:
free | awk 'NR==2{printf "%.0f", $3/$2*100}' -
Example Command:
free | awk 'NR==2{printf "%.0f", $3/$2*100}' -
Expected Output:
27 -
Flag & Command Breakdown:
free: Displays memory layout.awk: Processes line 2 (NR==2) and divides Used memory ($3) by Total memory ($2), multiplying by 100 to get a percentage.
38.5 Querying disk usage metrics using df parsing pipelines
Check disk usage on the root partition.
-
Syntax:
df [path] | awk 'NR==2 {print $5}' -
Example Command:
df / | awk 'NR==2 {print $5}' | sed 's/%//' -
Expected Output:
45 -
Flag & Command Breakdown:
df /: Checks the root disk mount.awk 'NR==2 {print $5}': Extracts the percentage column.sed 's/%//': Strips the%sign, leaving only the integer value for comparison.
38.6 Conditional branching: Evaluating values against thresholds
Compare the extracted system values against your defined thresholds:
if [ "$CPU_USAGE" -gt "$CPU_LIMIT" ]; then
trigger_alert "CPU usage high: $CPU_USAGE%"
fi
38.7 Creating alerts: Writing incidents to log files and syslogs
We use logger to write warning logs to the local system log manager.
-
Syntax:
logger [options] message -
Example Command:
logger -p user.warning "ALERT: CPU usage exceeded threshold" -
Expected Output: (No output in the console. Writes the warning message to
/var/log/syslog) -
Flag & Command Breakdown:
logger: Writes entries to system logs.-p user.warning: Sets the log priority level to “warning”.
38.8 Integrating email or Discord/Slack Webhook notifications
You can send alert logs to external messaging APIs using curl:
curl -H "Content-Type: application/json" \
-X POST \
-d "{\"content\": \"$ALERT_MESSAGE\"}" \
$DISCORD_WEBHOOK_URL
38.9 Designing the script to run continuously as a daemon process
To run the script as a daemon, wrap the logic in an infinite loop that runs every few seconds using sleep:
while true; do
check_system_health
sleep 60
done
38.10 Script validation, unit testing, and full code assembly
Assemble the full system health monitor script:
#!/bin/bash
set -u
CPU_LIMIT=80
MEM_LIMIT=85
DISK_LIMIT=90
# Calculate usage
CPU_IDLE=$(top -b -n 1 | grep "Cpu(s)" | awk '{print $8}')
CPU_USED=$(echo "100 - $CPU_IDLE" | bc | cut -d. -f1)
MEM_USED=$(free | awk 'NR==2{printf "%.0f", $3/$2*100}')
DISK_USED=$(df / | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$CPU_USED" -gt "$CPU_LIMIT" ]; then
logger -p user.warning "CRITICAL: CPU usage is at $CPU_USED%"
fi
if [ "$MEM_USED" -gt "$MEM_LIMIT" ]; then
logger -p user.warning "CRITICAL: Memory usage is at $MEM_USED%"
fi
if [ "$DISK_USED" -gt "$DISK_LIMIT" ]; then
logger -p user.warning "CRITICAL: Disk space usage is at $DISK_USED%"
fi
Chapter 39: Project 3 – File Organizer
39.1 Project Outline: Auto-sorting downloads and messy directories
This script parses a target folder (like a Downloads folder) and moves files into organized subfolders (e.g., Images, Documents, Archives) based on their file extensions.
39.2 Declaring Target Directories for Images, Documents, and Archives
Define target folder variables at the beginning of the script:
TARGET_DIR="/home/suresh/Downloads"
IMG_DIR="$TARGET_DIR/Images"
DOC_DIR="$TARGET_DIR/Documents"
ARC_DIR="$TARGET_DIR/Archives"
39.3 Checking and creating destination folders if they do not exist
Ensure target directories exist before moving files:
for folder in "$IMG_DIR" "$DOC_DIR" "$ARC_DIR"; do
if [ ! -d "$folder" ]; then
mkdir -p "$folder"
fi
done
39.4 Parsing files in a directory using Bash wildcards and loops
Iterate through the target files, ignoring directories:
for file in "$TARGET_DIR"/*; do
if [ -f "$file" ]; then
# Process the file
fi
done
39.5 Determining file extensions and file types using the file utility
The file utility identifies a file’s MIME type or format based on its contents rather than its extension.
-
Syntax:
file [options] filename -
Example Command:
file --mime-type -b logo.svg -
Expected Output:
image/svg+xml -
Flag & Command Breakdown:
file: Determines file type.--mime-type: Returns only the standard MIME format.-b: Brief mode; omits the filename from the output.
39.6 Implementing the sorting logic with mv and error handling
Move files into their respective folders based on their extensions:
ext="${file##*.}"
case "${ext,,}" in
jpg|png|gif|svg)
mv "$file" "$IMG_DIR/" ;;
pdf|docx|txt|xlsx)
mv "$file" "$DOC_DIR/" ;;
zip|tar|gz|xz)
mv "$file" "$ARC_DIR/" ;;
esac
39.7 Adding dry-run capability to preview changes before moving files
Use a command line flag to check what changes the script would make without moving files:
DRY_RUN=false
if [[ "${1:-}" == "--dry-run" ]]; then
DRY_RUN=true
fi
If $DRY_RUN is true, print the move commands instead of executing them.
39.8 Adding logs: Tracking files moved and generating summaries
Log operations to standard output or a file:
echo "[$(date)] Sorted file: $(basename "$file") -> $target_folder" >> "$TARGET_DIR/organizer.log"
39.9 Automating the script using cron scheduling or directory watching
To run the organizer automatically every hour, add it to your crontab:
0 * * * * /home/suresh/bin/organizer.sh --silent
39.10 Full Code Assembly and Script validation of the File Organizer
Save this script as organizer.sh:
#!/bin/bash
set -eu
TARGET_DIR="/home/suresh/Downloads"
DRY_RUN=false
# Check for dry-run option
if [ "${1:-}" = "--dry-run" ]; then
DRY_RUN=true
echo "=== DRY RUN MODE ==="
fi
# Create directories
mkdir -p "$TARGET_DIR/Images" "$TARGET_DIR/Documents" "$TARGET_DIR/Archives"
for file in "$TARGET_DIR"/*; do
# Skip directories
[ -d "$file" ] && continue
[ -f "$file" ] || continue
filename=$(basename "$file")
ext="${filename##*.}"
target_folder=""
case "${ext,,}" in
jpg|jpeg|png|gif|svg) target_folder="$TARGET_DIR/Images" ;;
pdf|doc|docx|txt|xlsx) target_folder="$TARGET_DIR/Documents" ;;
zip|tar|gz|tgz|xz) target_folder="$TARGET_DIR/Archives" ;;
*) continue ;; # Skip other file extensions
esac
if [ "$DRY_RUN" = true ]; then
echo "[DRY-RUN] Would move: $filename -> $target_folder"
else
mv "$file" "$target_folder/"
echo "Moved: $filename -> $target_folder"
fi
done