Last active 1 month ago

portalzine's Avatar portalzine revised this gist 1 month ago. Go to revision

1 file changed, 512 insertions

monitor_disk_stats.sh(file created)

@@ -0,0 +1,512 @@
1 +
2 + #!/bin/bash
3 +
4 + # Script to check comprehensive disk statistics
5 + # Author: Alex@portalZINE.de
6 + # Date: 25.11.2025
7 +
8 + #####################################
9 + # CONFIGURATION
10 + #####################################
11 + DEFAULT_DEVICE="/dev/sda1" # Default device to monitor
12 +
13 + # Ntfy Configuration
14 + NTFY_URL="https://your-ntfy-server.com/topic"
15 + # Leave the ":", when using a token
16 + NTFY_TOKEN=":your_token_here"
17 + # NTFY_TOKEN="user:password"
18 + NTFY_TITLE="Whatever Title You Want"
19 + #####################################
20 +
21 + # Initialize output variable
22 + OUTPUT=""
23 +
24 + # Function to add to output
25 + add_output() {
26 + OUTPUT="${OUTPUT}${1}\n"
27 + }
28 +
29 + # Function to display usage
30 + usage() {
31 + cat << EOF
32 + Usage: $(basename "$0") [OPTIONS] [DEVICE]
33 +
34 + Check comprehensive disk statistics for a device.
35 +
36 + OPTIONS:
37 + -h, --help Show this help message
38 + -d, --device DEVICE Specify device to check (e.g., /dev/sda1)
39 + -q, --quiet Quiet mode - minimal output
40 + -v, --verbose Verbose mode - show all available stats
41 + -s, --section NAME Show only specific section(s) (comma-separated)
42 + -l, --list-sections List all available sections and exit
43 + -n, --ntfy Send notification via ntfy (no console output)
44 + --ntfy-always Send ntfy notification AND show console output
45 +
46 + AVAILABLE SECTIONS:
47 + all Show all sections (default)
48 + basic Basic disk usage (size, used, available)
49 + inodes Inode usage statistics
50 + filesystem Filesystem information (type, blocks)
51 + mount Mount options
52 + device Device information (lsblk output)
53 + io I/O statistics (reads, writes, sectors)
54 + analysis Usage analysis and warnings
55 + consumers Top space consumers (largest directories)
56 +
57 + ARGUMENTS:
58 + DEVICE Device to check (default: $DEFAULT_DEVICE)
59 +
60 + NTFY CONFIGURATION:
61 + Configure ntfy settings at the top of the script:
62 + - NTFY_URL Ntfy server URL
63 + - NTFY_TOKEN Authentication token
64 + - NTFY_TITLE Notification title
65 +
66 + EXAMPLES:
67 + $(basename "$0") # Check default device ($DEFAULT_DEVICE)
68 + $(basename "$0") /dev/sdb1 # Check /dev/sdb1
69 + $(basename "$0") -d /dev/nvme0n1p1 # Check NVMe device
70 + $(basename "$0") --quiet /dev/sda2 # Quiet mode for /dev/sda2
71 + $(basename "$0") -s basic # Show only basic usage
72 + $(basename "$0") -s basic,analysis # Show basic usage and analysis
73 + $(basename "$0") -s io,consumers /dev/sdb1 # Show I/O stats and top consumers
74 + $(basename "$0") --list-sections # List all available sections
75 + $(basename "$0") --ntfy # Send notification only (no output)
76 + $(basename "$0") --ntfy-always # Send notification AND show output
77 +
78 + EOF
79 + exit 0
80 + }
81 +
82 + # Function to list all available sections
83 + list_sections() {
84 + cat << EOF
85 + Available Sections:
86 + ===================
87 +
88 + all - Show all sections (default behavior)
89 + basic - Basic disk usage information
90 + • Filesystem name
91 + • Total size
92 + • Used space
93 + • Available space
94 + • Usage percentage
95 + • Mount point
96 +
97 + inodes - Inode usage statistics
98 + • Total inodes
99 + • Used inodes
100 + • Free inodes
101 + • Inode usage percentage
102 +
103 + filesystem - Filesystem information
104 + • Filesystem type (ext4, xfs, etc.)
105 + • Block size
106 + • Total blocks
107 + • Free blocks
108 + • Available blocks
109 +
110 + mount - Mount options
111 + • Full mount command output
112 + • Mount flags (rw, relatime, etc.)
113 +
114 + device - Device information
115 + • Device name
116 + • Size
117 + • Type
118 + • Filesystem type
119 + • Mount point
120 + • Label
121 + • UUID
122 +
123 + io - I/O statistics
124 + • Read operations
125 + • Write operations
126 + • Sectors read/written
127 + • Read/write time
128 + • I/O operations in progress
129 + • Extended iostat (verbose mode only)
130 +
131 + analysis - Usage analysis
132 + • Disk space status (OK/WARNING/CRITICAL)
133 + • Inode status (OK/WARNING/CRITICAL)
134 + • Threshold-based alerts
135 +
136 + consumers - Top space consumers
137 + • Largest directories on the partition
138 + • Top 10 (normal) or top 20 (verbose)
139 + • Human-readable sizes
140 +
141 + Usage Examples:
142 + $(basename "$0") -s basic # Just the essentials
143 + $(basename "$0") -s basic,analysis # Quick health check
144 + $(basename "$0") -s io # I/O performance only
145 + $(basename "$0") -s all # Everything (same as no -s flag)
146 +
147 + EOF
148 + exit 0
149 + }
150 +
151 + # Parse command line arguments
152 + DEVICE=""
153 + QUIET_MODE=false
154 + VERBOSE_MODE=false
155 + SECTIONS=""
156 + NTFY_MODE=false
157 + NTFY_ALWAYS=false
158 +
159 + while [[ $# -gt 0 ]]; do
160 + case $1 in
161 + -h|--help)
162 + usage
163 + ;;
164 + -l|--list-sections)
165 + list_sections
166 + ;;
167 + -d|--device)
168 + DEVICE="$2"
169 + shift 2
170 + ;;
171 + -q|--quiet)
172 + QUIET_MODE=true
173 + shift
174 + ;;
175 + -v|--verbose)
176 + VERBOSE_MODE=true
177 + shift
178 + ;;
179 + -s|--section)
180 + SECTIONS="$2"
181 + shift 2
182 + ;;
183 + -n|--ntfy)
184 + NTFY_MODE=true
185 + shift
186 + ;;
187 + --ntfy-always)
188 + NTFY_ALWAYS=true
189 + shift
190 + ;;
191 + -*)
192 + echo "Error: Unknown option: $1"
193 + echo "Use -h or --help for usage information"
194 + exit 1
195 + ;;
196 + *)
197 + # Assume it's a device path
198 + if [ -z "$DEVICE" ]; then
199 + DEVICE="$1"
200 + else
201 + echo "Error: Multiple devices specified"
202 + exit 1
203 + fi
204 + shift
205 + ;;
206 + esac
207 + done
208 +
209 + # Use default device if none specified
210 + if [ -z "$DEVICE" ]; then
211 + DEVICE="$DEFAULT_DEVICE"
212 + fi
213 +
214 + # Function to check if a section should be displayed
215 + should_show_section() {
216 + local section=$1
217 +
218 + # If no sections specified, show all
219 + if [ -z "$SECTIONS" ]; then
220 + return 0
221 + fi
222 +
223 + # If 'all' is specified, show everything
224 + if [[ ",$SECTIONS," == *",all,"* ]]; then
225 + return 0
226 + fi
227 +
228 + # Check if section is in the comma-separated list
229 + if [[ ",$SECTIONS," == *",$section,"* ]]; then
230 + return 0
231 + fi
232 +
233 + return 1
234 + }
235 +
236 + add_output "========================================"
237 + add_output "Disk Statistics Report for $DEVICE"
238 + add_output "========================================"
239 + if [ "$QUIET_MODE" = false ]; then
240 + add_output "Report generated: $(date '+%Y-%m-%d %H:%M:%S')"
241 + fi
242 + add_output ""
243 +
244 + # Check if device exists
245 + if [ ! -b "$DEVICE" ]; then
246 + echo "Error: $DEVICE does not exist or is not a block device"
247 + exit 1
248 + fi
249 +
250 + # Get mount point
251 + MOUNT_POINT=$(df "$DEVICE" 2>/dev/null | awk 'NR==2 {print $6}')
252 +
253 + if [ -z "$MOUNT_POINT" ]; then
254 + echo "Error: $DEVICE is not mounted"
255 + exit 1
256 + fi
257 +
258 + # Quiet mode - show only essential info
259 + if [ "$QUIET_MODE" = true ]; then
260 + QUIET_INFO=$(df -h "$DEVICE" | awk 'NR==2 {
261 + print "Device: " $1
262 + print "Size: " $2
263 + print "Used: " $3
264 + print "Available: " $4
265 + print "Usage: " $5
266 + print "Mount: " $6
267 + }')
268 + add_output "$QUIET_INFO"
269 +
270 + USAGE_PERCENT=$(df "$DEVICE" | awk 'NR==2 {print $5}' | sed 's/%//')
271 + if [ "$USAGE_PERCENT" -ge 90 ]; then
272 + add_output "Status: ⚠️ CRITICAL (${USAGE_PERCENT}%)"
273 + elif [ "$USAGE_PERCENT" -ge 80 ]; then
274 + add_output "Status: ⚠️ WARNING (${USAGE_PERCENT}%)"
275 + else
276 + add_output "Status: ✓ OK (${USAGE_PERCENT}%)"
277 + fi
278 +
279 + # Print all output at once
280 + echo -e "$OUTPUT"
281 + exit 0
282 + fi
283 +
284 + if should_show_section "basic"; then
285 + add_output "========== BASIC DISK USAGE =========="
286 + BASIC_USAGE=$(df -h "$DEVICE" | awk 'NR==2 {
287 + print " Filesystem: " $1
288 + print " Total Size: " $2
289 + print " Used: " $3
290 + print " Available: " $4
291 + print " Usage: " $5
292 + print " Mount Point: " $6
293 + }')
294 + add_output "$BASIC_USAGE"
295 + add_output ""
296 + fi
297 +
298 + if should_show_section "inodes"; then
299 + add_output "========== INODE USAGE =========="
300 + INODE_USAGE=$(df -hi "$DEVICE" | awk 'NR==2 {
301 + print " Total Inodes: " $2
302 + print " Used Inodes: " $3
303 + print " Free Inodes: " $4
304 + print " Inode Usage: " $5
305 + }')
306 + add_output "$INODE_USAGE"
307 + add_output ""
308 + fi
309 +
310 + if should_show_section "filesystem"; then
311 + add_output "========== FILESYSTEM INFORMATION =========="
312 + # Filesystem type
313 + FS_TYPE=$(df -T "$DEVICE" | awk 'NR==2 {print $2}')
314 + add_output " Filesystem Type: $FS_TYPE"
315 +
316 + # Block size information
317 + BLOCK_SIZE=$(stat -f -c %s "$MOUNT_POINT" 2>/dev/null)
318 + if [ -n "$BLOCK_SIZE" ]; then
319 + add_output " Block Size: $BLOCK_SIZE bytes"
320 + fi
321 +
322 + # Total blocks
323 + TOTAL_BLOCKS=$(stat -f -c %b "$MOUNT_POINT" 2>/dev/null)
324 + FREE_BLOCKS=$(stat -f -c %f "$MOUNT_POINT" 2>/dev/null)
325 + AVAIL_BLOCKS=$(stat -f -c %a "$MOUNT_POINT" 2>/dev/null)
326 +
327 + if [ -n "$TOTAL_BLOCKS" ]; then
328 + add_output " Total Blocks: $TOTAL_BLOCKS"
329 + add_output " Free Blocks: $FREE_BLOCKS"
330 + add_output " Available: $AVAIL_BLOCKS"
331 + fi
332 + add_output ""
333 + fi
334 +
335 + if should_show_section "mount"; then
336 + add_output "========== MOUNT OPTIONS =========="
337 + MOUNT_INFO=$(mount | grep "$DEVICE")
338 + if [ -n "$MOUNT_INFO" ]; then
339 + add_output " $MOUNT_INFO"
340 + fi
341 + add_output ""
342 + fi
343 +
344 + if should_show_section "device"; then
345 + add_output "========== DISK DEVICE INFO =========="
346 + if command -v lsblk &> /dev/null; then
347 + LSBLK_INFO=$(lsblk "$DEVICE" -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,LABEL,UUID 2>/dev/null | sed 's/^/ /')
348 + add_output "$LSBLK_INFO"
349 + fi
350 + add_output ""
351 + fi
352 +
353 + if should_show_section "io"; then
354 + add_output "========== I/O STATISTICS =========="
355 + if [ -f /proc/diskstats ]; then
356 + # Extract device name without /dev/ prefix
357 + DEVICE_NAME=$(basename "$DEVICE")
358 + # Extract stats for the device
359 + DISK_STATS=$(grep " $DEVICE_NAME " /proc/diskstats)
360 + if [ -n "$DISK_STATS" ]; then
361 + IO_STATS=$(echo "$DISK_STATS" | awk '{
362 + print " Device: " $3
363 + print " Reads: " $4
364 + print " Reads Merged: " $5
365 + print " Sectors Read: " $6
366 + print " Read Time (ms): " $7
367 + print " Writes: " $8
368 + print " Writes Merged: " $9
369 + print " Sectors Written: " $10
370 + print " Write Time (ms): " $11
371 + print " I/Os in Progress:" $12
372 + print " I/O Time (ms): " $13
373 + }')
374 + add_output "$IO_STATS"
375 + fi
376 + fi
377 +
378 + # iostat if available - show in verbose mode or if explicitly installed
379 + if command -v iostat &> /dev/null && [ "$VERBOSE_MODE" = true ]; then
380 + add_output ""
381 + add_output " Extended I/O Stats:"
382 + IOSTAT_INFO=$(iostat -x "$DEVICE" 1 1 2>/dev/null | tail -n +4 | sed 's/^/ /')
383 + add_output "$IOSTAT_INFO"
384 + fi
385 + add_output ""
386 + fi
387 +
388 + if should_show_section "analysis"; then
389 + add_output "========== USAGE ANALYSIS =========="
390 +
391 + # Get usage percentage as a number
392 + USAGE_PERCENT=$(df "$DEVICE" | awk 'NR==2 {print $5}' | sed 's/%//')
393 + INODE_PERCENT=$(df -i "$DEVICE" | awk 'NR==2 {print $5}' | sed 's/%//')
394 +
395 + # Disk space warning
396 + add_output " Disk Space Status:"
397 + if [ "$USAGE_PERCENT" -ge 90 ]; then
398 + add_output " ⚠️ CRITICAL: Disk usage is critically high (${USAGE_PERCENT}%)!"
399 + elif [ "$USAGE_PERCENT" -ge 80 ]; then
400 + add_output " ⚠️ WARNING: Disk usage is high (${USAGE_PERCENT}%)"
401 + elif [ "$USAGE_PERCENT" -ge 70 ]; then
402 + add_output " ⚡ NOTICE: Disk usage is moderate (${USAGE_PERCENT}%)"
403 + else
404 + add_output " ✓ OK: Disk usage is within normal range (${USAGE_PERCENT}%)"
405 + fi
406 +
407 + # Inode warning
408 + add_output ""
409 + add_output " Inode Status:"
410 + if [ "$INODE_PERCENT" -ge 90 ]; then
411 + add_output " ⚠️ CRITICAL: Inode usage is critically high (${INODE_PERCENT}%)!"
412 + elif [ "$INODE_PERCENT" -ge 80 ]; then
413 + add_output " ⚠️ WARNING: Inode usage is high (${INODE_PERCENT}%)"
414 + else
415 + add_output " ✓ OK: Inode usage is within normal range (${INODE_PERCENT}%)"
416 + fi
417 + add_output ""
418 + fi
419 +
420 + if should_show_section "consumers"; then
421 + add_output "========== TOP SPACE CONSUMERS =========="
422 + if [ -d "$MOUNT_POINT" ]; then
423 + add_output " Largest directories in $MOUNT_POINT:"
424 + if [ "$VERBOSE_MODE" = true ]; then
425 + # Show top 20 in verbose mode
426 + TOP_DIRS=$(du -h "$MOUNT_POINT" --max-depth=1 2>/dev/null | sort -rh | head -20 | sed 's/^/ /')
427 + else
428 + # Show top 10 in normal mode
429 + TOP_DIRS=$(du -h "$MOUNT_POINT" --max-depth=1 2>/dev/null | sort -rh | head -10 | sed 's/^/ /')
430 + fi
431 + add_output "$TOP_DIRS"
432 + fi
433 + add_output ""
434 + fi
435 +
436 + add_output "========================================"
437 + add_output "Report completed: $(date '+%Y-%m-%d %H:%M:%S')"
438 + add_output "========================================"
439 +
440 + # Function to send ntfy notification
441 + send_ntfy() {
442 + local message="$1"
443 + local priority="${2:-default}"
444 + local tags="${3:-}"
445 +
446 + if [ -z "$NTFY_URL" ] || [ -z "$NTFY_TOKEN" ]; then
447 + echo "Error: Ntfy configuration is incomplete"
448 + return 1
449 + fi
450 +
451 + local curl_args=(-u "$NTFY_TOKEN")
452 +
453 + if [ -n "$NTFY_TITLE" ]; then
454 + curl_args+=(-H "Title: $NTFY_TITLE")
455 + fi
456 +
457 + if [ -n "$priority" ] && [ "$priority" != "default" ]; then
458 + curl_args+=(-H "Priority: $priority")
459 + fi
460 +
461 + if [ -n "$tags" ]; then
462 + curl_args+=(-H "Tags: $tags")
463 + fi
464 +
465 + curl_args+=(-d "$message" "$NTFY_URL")
466 +
467 + curl -s "${curl_args[@]}" > /dev/null
468 + return $?
469 + }
470 +
471 + # Prepare notification if ntfy is enabled
472 + if [ "$NTFY_MODE" = true ] || [ "$NTFY_ALWAYS" = true ]; then
473 + # Get key metrics for determining priority
474 + USAGE_PERCENT=$(df "$DEVICE" | awk 'NR==2 {print $5}' | sed 's/%//')
475 + INODE_PERCENT=$(df -i "$DEVICE" | awk 'NR==2 {print $5}' | sed 's/%//')
476 +
477 + # Determine priority and tags based on usage
478 + PRIORITY="default"
479 + TAGS="white_check_mark"
480 +
481 + if [ "$USAGE_PERCENT" -ge 90 ] || [ "$INODE_PERCENT" -ge 90 ]; then
482 + PRIORITY="urgent"
483 + TAGS="warning,skull"
484 + elif [ "$USAGE_PERCENT" -ge 80 ] || [ "$INODE_PERCENT" -ge 80 ]; then
485 + PRIORITY="high"
486 + TAGS="warning"
487 + elif [ "$USAGE_PERCENT" -ge 70 ] || [ "$INODE_PERCENT" -ge 70 ]; then
488 + PRIORITY="default"
489 + TAGS="zap"
490 + fi
491 +
492 + # Send the full output as notification
493 + NTFY_MESSAGE=$(echo -e "$OUTPUT")
494 +
495 + # Send notification
496 + if send_ntfy "$NTFY_MESSAGE" "$PRIORITY" "$TAGS"; then
497 + if [ "$NTFY_MODE" = true ]; then
498 + echo "Notification sent successfully to ntfy"
499 + exit 0
500 + fi
501 + else
502 + echo "Error: Failed to send ntfy notification"
503 + if [ "$NTFY_MODE" = true ]; then
504 + exit 1
505 + fi
506 + fi
507 + fi
508 +
509 + # Print all output at once at the end (unless ntfy-only mode)
510 + if [ "$NTFY_MODE" = false ]; then
511 + echo -e "$OUTPUT"
512 + fi
Newer Older