Implementing CPU, Memory, and Disk Monitoring with Shell Scripts

Implementing CPU, Memory, and Disk Monitoring with Shell Scripts

In modern operations management systems, real-time monitoring of server resource status serves as the cornerstone for ensuring business continuity. Traditional passive alert modes often suffer from latency, failing to issue warnings before resources are exhausted. By writing efficient shell scripts, we can leverage Linux's powerful command-line toolset to actively collect and visualize CPU, memory, and disk utilization rates. This article delves into how to construct a lightweight, scalable server monitoring solution using shell scripts, helping operations personnel quickly grasp the health status of their systems.
Before writing monitoring scripts, it is essential to understand how the Linux kernel exposes hardware resource usage at its core level. The Linux kernel provides rich interfaces through the /proc filesystem, allowing user-space programs to read kernel data directly without installing additional agent software.

  • CPU Monitoring: Primarily obtained via the /proc/stat file, which contains statistical information for each CPU core. This file records various CPU usage states over different time intervals (such as us for user mode, sy for system mode, and id for idle state). By calculating the difference in values between two consecutive reads divided by the elapsed time, one can derive the true CPU utilization rate.
  • Memory Monitoring: Core data resides in the /proc/meminfo file. Key metrics include MemTotal (total memory), MemFree (free memory), and MemAvailable (truly available memory). The latter provides a more accurate reflection of whether the system is at risk of running out of memory compared to the former, because Linux supports swap partitions; free memory does not necessarily mean it can be used immediately.
  • Disk Monitoring: The most common tool is the df -h command, which reads /proc/mounts or filesystem metadata to calculate used space, total capacity, and percentage for each mount point. For scenarios requiring finer-grained information, one can also combine this with iostat to view I/O wait times.

Script Implementation: CPU Utilization Calculation Logic

Calculating CPU utilization is arguably the most challenging part of shell monitoring due to its reliance on cross-temporal state differential operations. Below is a simplified example of CPU monitoring logic:

  1. Read Data: Call cat /proc/stat to retrieve the current CPU statistics line.
  2. Extract Fields: Parse the output to extract numerical values for fields such as us, sy, id, and wa (wait time).
  3. Timestamp Calculation: Use date +%s to obtain a second-level timestamp, utilizing the difference between two reads as the denominator.
  4. Differential Operation: Subtract current values from previous round's values to get increments.
  5. Percentage Conversion: Divide the increment of (user mode + system mode + wait time) by the total increment plus idle increment, then multiply by 100.

It is important to note that since /proc/stat refreshes once per second by default, the calculated CPU utilization typically represents an average value. This holds significant reference for determining whether sudden workload spikes are excessive.

Script Implementation: Memory and Disk Status Collection

Compared to CPU monitoring, memory and disk logic is more direct, primarily relying on parsing text files or executing standard commands.

Memory Monitoring Example

By reading /proc/meminfo, extract the MemAvailable field and calculate utilization. If available memory drops below 20% of total memory, trigger a warning mechanism. This method avoids false positives caused by swap partitions and is recommended for production environments.

Disk Monitoring Example

Utilize the df -h command in conjunction with regular expressions or the awk tool for parsing. The script will iterate through all mount points (excluding tmpfs and devtmpfs) to identify partitions exceeding a threshold (e.g., 85%) and output their specific paths along with remaining space sizes.

Practical Exercise: Comprehensive Monitoring Script Development

Integrating the above logic into a complete shell script can significantly boost operational efficiency. Below is a basic implementation framework named system_monitor.sh:

#!/bin/bash

# Define thresholds
CPU_THRESHOLD=80
MEM_THRESHOLD=20
DISK_THRESHOLD=85

echo "=== Starting System Health Check ===" | tee /dev/stderr

# --- CPU Monitoring ---
read -r cpu_line < /proc/stat
prev_cpu_line=$(cat /proc/stat | head -n1)

# Extract current values (us, sy, id, wa)
curr_us=$(echo $cpu_line | awk '{print $2}')
curr_sy=$(echo $cpu_line | awk '{print $3}')
curr_id=$(echo $cpu_line | awk '{print $4}')
curr_wa=$(echo $cpu_line | awk '{print $5}')

# Extract previous round values
prev_us=$(echo $prev_cpu_line | awk '{print $2}')
prev_sy=$(echo $prev_cpu_line | awk '{print $3}')
prev_id=$(echo $prev_cpu_line | awk '{print $4}')
prev_wa=$(echo $prev_cpu_line | awk '{print $5}')

# Calculate time difference (simplified version, assuming fixed interval of 1 second)
time_diff=1

user_time=$((curr_us - prev_us))
sys_time=$((curr_sy - prev_sy))
idle_time=$((curr_id - prev_id))
wait_time=$((curr_wa - prev_wa))

total_active=$((user_time + sys_time + wait_time))
total_idle=$idle_time
total_cpu=$((total_active + total_idle))

cpu_usage=$(awk "BEGIN {printf \"%.2f\", ($total_active / $total_cpu) * 100}")

if (( $(echo "$cpu_usage > $CPU_THRESHOLD" | bc -l) )); then
    echo "[Warning] CPU utilization too high: ${cpu_usage}%"
fi

# --- Memory Monitoring ---
mem_available=$(grep MemAvailable /proc/meminfo | awk '{print $2}')
mem_total=$(grep MemTotal /proc/meminfo | awk '{print $2}')
mem_used=$((mem_total - mem_available))
mem_percent=$(awk "BEGIN {printf \"%.2f\", ($mem_used / $mem_total) * 100}")

if (( $(echo "$mem_percent > $MEM_THRESHOLD" | bc -l) )); then
    echo "[Warning] Memory utilization too high: ${mem_percent}%"
fi

# --- Disk Monitoring ---
df --output=source,pcent,used | tail -n +2 | awk -v thresh=$DISK_THRESHOLD '$3 > thresh {print "Disk [" $1 "] Usage Rate:" $4 "%"}'

echo "=== Check Completed ==="

Deployment and Maintenance: Logging and Scalability

After writing the script, details regarding actual deployment often determine its long-term value. First, it is recommended to redirect output to a log file (e.g., /var/log/system_monitor.log) for historical trend analysis and fault tracing. Secondly, to adapt to multi-core CPU environments or complex mount structures, the script should enhance robustness in handling awk and grep, preventing parsing errors caused by format changes.

Furthermore, this script exhibits good scalability. If network bandwidth monitoring (via /proc/net/dev) or load average (uptime command) is required, new detection modules can be added following the existing modular logic without major restructuring. For scenarios requiring higher frequency collection, consider running the script within a cron scheduled task or integrating it into mature monitoring systems like Prometheus Node Exporter, using shell scripts as a preliminary data cleaning layer to further reduce system load.

Conclusion

Shell scripts, due to their simplicity and native support, have become the preferred tool for basic resource monitoring in Linux environments. By mastering techniques for reading /proc filesystems and fundamental text processing commands, operations personnel can construct real-time resource awareness capabilities at a low cost. This solution is not only applicable to single-server setups but its logical architecture can also be easily migrated to containerized environments or cluster management scenarios, providing solid data support for automated operations systems.