Complete Guide to Bash df Command

Introduction to df

The df (disk free) command is a fundamental Unix/Linux utility for reporting file system disk space usage. It provides essential information about mounted file systems, including total size, used space, available space, and usage percentages. Understanding df thoroughly is crucial for system administration, capacity planning, and monitoring disk usage.

Key Concepts

  • File Systems: Mounted partitions and their mount points
  • Disk Usage: Total, used, and available space
  • Inodes: File system metadata storage
  • Human-Readable: Formatted output for easy understanding
  • Local vs Remote: Can show both local and network file systems

1. Basic Usage

Simple Disk Space Reports

#!/bin/bash
# Basic df command (shows all mounted file systems)
df
# Show specific file system
df /home
df /dev/sda1
# Show multiple file systems
df /home /var /tmp
# Human-readable format (with -h)
df -h
df --human-readable
# Show in kilobytes (default)
df -k
# Show in megabytes
df -m
# Show in gigabytes
df -h  # Already human-readable, but can force specific
df -B G  # Show in gigabytes (GNU)
# Examples
echo "=== Disk Usage Report ==="
df -h
echo
echo "=== Root Filesystem ==="
df -h /

Common Examples

#!/bin/bash
# Check root filesystem usage
df -h /
# Check home directory usage
df -h /home
# Check current directory's filesystem
df -h .
# Show all filesystems including tmpfs
df -h -a
# Exclude specific filesystem types
df -h -x tmpfs -x devtmpfs
# Include only specific types
df -h -t ext4 -t xfs
# Show with total
df -h --total
# Create a report
cat << EOF
System Disk Usage Report
=======================
Date: $(date)
Host: $(hostname)
$(df -h)
Top Usage:
$(df -h | sort -k5 -r | head -5)
EOF

2. Essential Options

Common Options Reference

#!/bin/bash
# -a, --all: Include dummy file systems
df -a
# -h, --human-readable: Print sizes in human readable format
df -h
# -H: Human-readable but with powers of 1000 (SI)
df -H
# -k: Use 1K blocks
df -k
# -m: Use 1M blocks
df -m
# -B, --block-size: Specify block size
df -B 1K
df -B 1M
df -B 1G
# -t, --type: Limit to filesystem type
df -t ext4
df -t xfs
df -t tmpfs
# -x, --exclude-type: Exclude filesystem type
df -x tmpfs
df -x devtmpfs
# -T, --print-type: Print filesystem type
df -T
# -l, --local: Only show local filesystems
df -l
# --total: Show total
df --total
# -i, --inodes: Show inode information instead of blocks
df -i
# --sync: Sync before getting usage
df --sync
# -P, --portability: POSIX output format
df -P
# Combine options
df -hT
df -hl --total
df -h -t ext4 -t xfs

Practical Option Combinations

#!/bin/bash
# Comprehensive system view
df -hT --total
# Local filesystems only with types
df -lhT
# Check filesystems with >80% usage
df -h | awk 'NR>1 {gsub(/%/,"",$5); if($5>80) print $0}'
# Show inodes usage
df -hi
# Monitor specific mount points
df -h / /home /var
# Check network filesystems
df -h -t nfs -t cifs
# Format for monitoring
df --output='source,fstype,size,used,avail,pcent,target' -h
# Watch disk usage changes
watch -n 60 df -h

3. Understanding Output

Column Explanations

#!/bin/bash
# Standard df output
df -h
# Output columns:
# Filesystem      Size  Used  Avail  Use%  Mounted on
# /dev/sda1        98G   45G   48G   49%   /
# With -T (filesystem type)
df -hT
# Filesystem     Type      Size  Used  Avail  Use%  Mounted on
# /dev/sda1      ext4       98G   45G   48G   49%   /
# With -i (inodes)
df -hi
# Filesystem     Inodes  IUsed  IFree  IUse%  Mounted on
# /dev/sda1        6.5M   235K   6.3M    4%   /
# Parse specific columns
df -h | awk '{print $1, $5, $6}'  # Filesystem, Use%, Mount
# Get usage percentage of root
df / | awk 'NR==2 {print $5}' | tr -d '%'
# Check if any filesystem is over threshold
df -h | awk 'NR>1 && $5+0 > 90 {print "Warning: " $6 " is " $5 " full"}'

Interpreting Values

#!/bin/bash
# Reserved blocks (typically 5% for root)
tune2fs -l /dev/sda1 | grep "Reserved block count"
# Why used + avail != total?
# - Reserved blocks for root
# - Filesystem metadata
# - Rounding in human-readable output
# Calculate actual usage including reserved
df_actual() {
local fs="$1"
df -k "$fs" | awk 'NR==2 {
total=$2
used=$3
avail=$4
reserved = total - used - avail
pct_used = (used/total)*100
pct_with_reserve = ((used+reserved)/total)*100
printf "Filesystem: %s\n", $1
printf "Total: %d KB\n", total
printf "Used: %d KB (%.1f%%)\n", used, pct_used
printf "Available: %d KB\n", avail
printf "Reserved: %d KB (%.1f%%)\n", reserved, (reserved/total)*100
}'
}
# Usage
df_actual /

4. Working with Different Filesystem Types

Filtering by Type

#!/bin/bash
# List all available filesystem types
cat /proc/filesystems
# Show only ext4 filesystems
df -t ext4 -h
# Show only XFS filesystems
df -t xfs -h
# Show only tmpfs (temporary filesystems)
df -t tmpfs -h
# Exclude pseudo filesystems
df -x tmpfs -x devtmpfs -x squashfs -h
# Show network filesystems
df -t nfs -t nfs4 -t cifs -h
# Multiple types
df -t ext4 -t xfs -t btrfs -h
# Usage example
echo "=== Local Filesystems ==="
df -l -h
echo -e "\n=== Network Filesystems ==="
df -t nfs -t cifs -h 2>/dev/null || echo "No network filesystems"
echo -e "\n=== Temporary Filesystems ==="
df -t tmpfs -h

Special Filesystems

#!/bin/bash
# Show all filesystems including special ones
df -a -h
# Common special filesystems to ignore
# - devtmpfs: Device files
# - tmpfs: Temporary filesystems
# - squashfs: Read-only compressed filesystems
# - overlay: Overlay filesystems (containers)
# Clean output without pseudo filesystems
df -h -x tmpfs -x devtmpfs -x squashfs -x overlay
# Docker/container filesystems
df -h -t overlay
# Check specific mount types
check_mount_type() {
local mount_point="$1"
mount | grep " $mount_point "
}
# Example
check_mount_type "/home"

5. Inode Usage

Understanding Inodes

#!/bin/bash
# Show inode usage
df -i
df -hi  # Human-readable
# Check inode usage on specific filesystem
df -i /home
# Find filesystems with high inode usage
df -i | awk 'NR>1 {gsub(/%/,"",$5); if($5>80) print $0}'
# Watch inode usage
watch -n 60 'df -hi | grep -v tmpfs'
# Check inode limits
tune2fs -l /dev/sda1 | grep -E "Inode count|Inodes per group"
# Common scenarios for inode exhaustion
# - Many small files (mail spools, cache directories)
# - Deep directory structures
# - Source code repositories
# Find directories with many files
find / -xdev -type d -exec sh -c '
count=$(find "$0" -maxdepth 1 -type f | wc -l)
if [ $count -gt 10000 ]; then
echo "$0: $count files"
fi
' {} \; 2>/dev/null
# Check inode usage percentage
inode_usage() {
local mount="$1"
df -i "$mount" | awk 'NR==2 {print $5}' | tr -d '%'
}
# Example
if [ $(inode_usage /home) -gt 90 ]; then
echo "Warning: Inode usage high on /home"
fi

Inode Troubleshooting

#!/bin/bash
# Find filesystems running out of inodes
check_inodes() {
local threshold="${1:-80}"
df -i | awk -v thresh="$threshold" '
NR>1 {
gsub(/%/,"",$5)
if ($5 > thresh) {
printf "WARNING: %s is at %d%% inode usage\n", $1, $5
}
}
'
}
# Find filesystems with many files
find_inode_heavy() {
local mount_point="$1"
echo "Top directories by file count on $mount_point:"
find "$mount_point" -xdev -type d -print0 2>/dev/null | 
xargs -0 -I {} sh -c 'echo $(find "{}" -type f | wc -l) "{}"' |
sort -rn | head -20
}
# Clear inode usage by removing old files
cleanup_old_files() {
local dir="$1"
local days="${2:-30}"
echo "Removing files older than $days days in $dir"
find "$dir" -type f -mtime +"$days" -delete
find "$dir" -type d -empty -delete
}
# Monitor inode usage over time
monitor_inodes() {
local log_file="/var/log/inode_usage.log"
while true; do
echo "$(date): $(df -i /home | tail -1)" >> "$log_file"
sleep 3600  # Check every hour
done
}

6. Script Examples

Disk Space Monitoring Script

#!/bin/bash
# Comprehensive disk space monitor
# Configuration
THRESHOLD=80
EMAIL="[email protected]"
LOG_FILE="/var/log/disk_monitor.log"
EXCLUDE_TYPES="tmpfs devtmpfs squashfs overlay"
# Colors for output
RED='\033[0;31m'
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
echo -e "${2:-$GREEN}$1${NC}"
}
check_disk_usage() {
local issues=0
# Build exclude pattern
local exclude_args=()
for type in $EXCLUDE_TYPES; do
exclude_args+=(-x "$type")
done
df -h "${exclude_args[@]}" | tail -n +2 | while read line; do
# Parse line
filesystem=$(echo "$line" | awk '{print $1}')
size=$(echo "$line" | awk '{print $2}')
used=$(echo "$line" | awk '{print $3}')
avail=$(echo "$line" | awk '{print $4}')
use_pct=$(echo "$line" | awk '{print $5}' | tr -d '%')
mount=$(echo "$line" | awk '{print $6}')
# Check usage
if [ "$use_pct" -gt "$THRESHOLD" ]; then
issues=$((issues + 1))
# Critical or warning?
if [ "$use_pct" -gt 90 ]; then
level="CRITICAL"
color="$RED"
else
level="WARNING"
color="$YELLOW"
fi
message="$level: $mount ($filesystem) is ${use_pct}% full"
log "$message" "$color"
# Send alert
if [ "$use_pct" -gt 90 ]; then
echo "$message" | mail -s "Disk Alert: $mount" "$EMAIL"
fi
# Show top directories
if [ "$use_pct" -gt 85 ]; then
log "Top directories on $mount:" "$YELLOW"
du -sh "$mount"/* 2>/dev/null | sort -rh | head -5 >> "$LOG_FILE"
fi
fi
done
return $issues
}
check_inode_usage() {
local issues=0
df -i | tail -n +2 | while read line; do
use_pct=$(echo "$line" | awk '{print $5}' | tr -d '%')
mount=$(echo "$line" | awk '{print $6}')
filesystem=$(echo "$line" | awk '{print $1}')
# Skip pseudo filesystems
if [[ "$filesystem" == *"tmpfs"* ]] || [[ "$filesystem" == *"devtmpfs"* ]]; then
continue
fi
if [ "$use_pct" -gt "$THRESHOLD" ]; then
issues=$((issues + 1))
log "WARNING: Inode usage on $mount is ${use_pct}% full" "$YELLOW"
fi
done
return $issues
}
generate_report() {
local report="/tmp/disk_report.$$"
cat > "$report" << EOF
Disk Space Report
================
Generated: $(date)
Hostname: $(hostname)
=== Filesystem Usage ===
$(df -h -x tmpfs -x devtmpfs)
=== Inode Usage ===
$(df -hi -x tmpfs -x devtmpfs)
=== Largest Directories (/) ===
$(du -sh /* 2>/dev/null | sort -rh | head -10)
=== Mounted Filesystems ===
$(mount | column -t)
EOF
cat "$report"
}
# Main
main() {
log "Starting disk space check"
local disk_issues=0
local inode_issues=0
check_disk_usage
disk_issues=$?
check_inode_usage
inode_issues=$?
if [ $disk_issues -gt 0 ] || [ $inode_issues -gt 0 ]; then
log "Found issues: $disk_issues disk, $inode_issues inode" "$YELLOW"
generate_report | mail -s "Disk Report for $(hostname)" "$EMAIL"
else
log "All filesystems OK" "$GREEN"
fi
# Archive report
generate_report >> "$LOG_FILE"
}
# Run main
main

Capacity Planning Tool

#!/bin/bash
# Disk capacity planning tool
CAPACITY_LOG="/var/log/capacity_history.log"
TREND_DAYS=30
# Record current usage
record_usage() {
local timestamp=$(date +%s)
df -k | tail -n +2 | while read line; do
filesystem=$(echo "$line" | awk '{print $1}')
used=$(echo "$line" | awk '{print $3}')
mount=$(echo "$line" | awk '{print $6}')
echo "$timestamp|$filesystem|$used|$mount" >> "$CAPACITY_LOG"
done
}
# Analyze growth trend
analyze_trend() {
local mount="$1"
local days="${2:-30}"
local cutoff=$(($(date +%s) - days * 86400))
# Get historical data
local data=$(grep "|$mount$" "$CAPACITY_LOG" | awk -F'|' -v cutoff="$cutoff" '
$1 >= cutoff {
print $1, $3
}
' | sort -n)
if [ -z "$data" ]; then
echo "No historical data for $mount"
return 1
fi
# Calculate daily growth rate
local first_usage=$(echo "$data" | head -1 | awk '{print $2}')
local last_usage=$(echo "$data" | tail -1 | awk '{print $2}')
local first_time=$(echo "$data" | head -1 | awk '{print $1}')
local last_time=$(echo "$data" | tail -1 | awk '{print $1}')
local days_diff=$(( (last_time - first_time) / 86400 ))
if [ $days_diff -lt 1 ]; then
days_diff=1
fi
local growth_rate=$(( (last_usage - first_usage) / days_diff ))
local total_size=$(df -k "$mount" | tail -1 | awk '{print $2}')
local current_usage=$(df -k "$mount" | tail -1 | awk '{print $3}')
echo "Analysis for $mount:"
echo "  Current usage: $(numfmt --to=iec $((current_usage * 1024)))"
echo "  Total size: $(numfmt --to=iec $((total_size * 1024)))"
echo "  Growth rate: $(numfmt --to=iec $((growth_rate * 1024)))/day"
if [ $growth_rate -gt 0 ]; then
local days_until_full=$(( (total_size - current_usage) / growth_rate ))
echo "  Days until full: $days_until_full"
if [ $days_until_full -lt 30 ]; then
echo "  ⚠️  WARNING: Less than 30 days remaining!"
elif [ $days_until_full -lt 90 ]; then
echo "  ⚠️  Less than 90 days remaining"
fi
fi
}
# Predict future usage
predict_usage() {
local mount="$1"
local days_ahead="${2:-30}"
local current=$(df -k "$mount" | tail -1 | awk '{print $3}')
local total=$(df -k "$mount" | tail -1 | awk '{print $2}')
# Get growth rate from trend
local growth_rate=$(analyze_trend "$mount" 30 | grep "Growth rate" | awk '{print $3}')
if [ -n "$growth_rate" ]; then
local predicted=$((current + (growth_rate * days_ahead)))
local pct=$(( predicted * 100 / total ))
echo "Prediction for $days_ahead days:"
echo "  Current: $(numfmt --to=iec $((current * 1024)))"
echo "  Predicted: $(numfmt --to=iec $((predicted * 1024))) (${pct}%)"
if [ $pct -gt 90 ]; then
echo "  ⚠️  WARNING: Predicted usage exceeds 90%"
fi
fi
}
# Main menu
while true; do
clear
echo "=== Capacity Planning Tool ==="
echo "1. Record current usage"
echo "2. Show growth trends"
echo "3. Predict future usage"
echo "4. Generate report"
echo "5. Exit"
read -p "Select option: " opt
case $opt in
1)
record_usage
echo "Usage recorded"
sleep 2
;;
2)
df -h | tail -n +2 | awk '{print $6}' | while read mount; do
analyze_trend "$mount"
echo
done
read -p "Press enter to continue"
;;
3)
df -h | tail -n +2 | awk '{print $6}' | while read mount; do
predict_usage "$mount" 30
echo
done
read -p "Press enter to continue"
;;
4)
{
echo "Capacity Planning Report"
echo "========================"
echo "Date: $(date)"
echo
df -h
echo
df -h | tail -n +2 | awk '{print $6}' | while read mount; do
analyze_trend "$mount" 30
echo
done
} > capacity_report.txt
echo "Report saved to capacity_report.txt"
sleep 2
;;
5)
exit 0
;;
esac
done

7. Integration with Other Commands

Using with awk, grep, sort

#!/bin/bash
# Find filesystems with usage > 80%
df -h | awk 'NR>1 && $5+0 > 80 {print $0}'
# Sort by usage percentage
df -h | tail -n +2 | sort -k5 -r
# Show only specific columns
df -h | awk '{print $1, $5, $6}' | column -t
# Create summary with awk
df -h | awk '
BEGIN {print "Filesystem Summary"}
NR>1 {
used[$6] = $3
total[$6] = $2
pct[$6] = $5
}
END {
for (m in used) {
printf "%-20s %-8s %-8s %s\n", m, total[m], used[m], pct[m]
}
}'
# Find and sort by available space
df -h | tail -n +2 | sort -k4 -r
# Exclude certain filesystems
df -h | grep -Ev '(tmpfs|devtmpfs|udev)'
# Format as CSV
df -h | awk '
BEGIN {print "Filesystem,Size,Used,Avail,Use%,Mounted"}
NR>1 {
gsub(/%/,"",$5)
printf "%s,%s,%s,%s,%d,%s\n", $1, $2, $3, $4, $5, $6
}'

With find and du

#!/bin/bash
# Find large directories on filesystems with low space
check_large_dirs() {
local threshold="${1:-80}"
df -h | awk -v thresh="$threshold" 'NR>1 && $5+0 > thresh {print $6}' | while read mount; do
echo "Large directories on $mount:"
du -sh "$mount"/* 2>/dev/null | sort -rh | head -10
echo
done
}
# Find filesystems with specific usage patterns
find_filesystems() {
local usage_min="${1:-0}"
local usage_max="${2:-100}"
df -h | awk -v min="$usage_min" -v max="$usage_max" '
NR>1 {
gsub(/%/,"",$5)
if ($5 >= min && $5 <= max) {
print $0
}
}
'
}
# Monitor filesystem changes
monitor_changes() {
local log_file="/tmp/fs_changes.log"
local snapshot1="/tmp/fs_snapshot1"
local snapshot2="/tmp/fs_snapshot2"
# Take first snapshot
df -k > "$snapshot1"
sleep 60
# Take second snapshot
df -k > "$snapshot2"
# Compare
diff "$snapshot1" "$snapshot2" | grep -E '^[<>]' | while read line; do
echo "$(date): $line" >> "$log_file"
done
}

8. Performance Monitoring

Real-time Monitoring

#!/bin/bash
# Watch disk usage in real-time
watch -n 5 'df -h | grep -v tmpfs'
# Monitor specific mount point with alerts
monitor_mount() {
local mount="$1"
local interval="${2:-10}"
local threshold="${3:-90}"
while true; do
usage=$(df -h "$mount" | tail -1 | awk '{print $5}' | tr -d '%')
if [ "$usage" -gt "$threshold" ]; then
echo "$(date): WARNING - $mount is ${usage}% full"
fi
sleep "$interval"
done
}
# Graph disk usage over time
graph_usage() {
local mount="$1"
local points="${2:-60}"
local interval="${3:-10}"
for i in $(seq 1 $points); do
usage=$(df -h "$mount" | tail -1 | awk '{print $5}' | tr -d '%')
bars=$((usage / 2))
printf "%3d%% [%s%s]\n" $usage \
"$(printf '█%.0s' $(seq 1 $bars))" \
"$(printf '░%.0s' $(seq 1 $((50 - bars))))"
sleep "$interval"
done
}
# Usage
# monitor_mount / 30 85
# graph_usage / 100 5

Historical Tracking

#!/bin/bash
# Log disk usage to database (simple flat file)
LOG_DIR="/var/log/disk_usage"
mkdir -p "$LOG_DIR"
log_usage() {
local timestamp=$(date +%s)
local date=$(date +%Y%m%d)
df -k | tail -n +2 | while read line; do
filesystem=$(echo "$line" | awk '{print $1}')
used=$(echo "$line" | awk '{print $3}')
mount=$(echo "$line" | awk '{print $6}' | tr '/' '_')
echo "$timestamp $used" >> "$LOG_DIR/${date}_${mount}.log"
done
}
# Analyze historical data
analyze_history() {
local mount_point="$1"
local days="${2:-7}"
local mount_safe=$(echo "$mount_point" | tr '/' '_')
echo "Analysis for $mount_point over last $days days:"
echo "Date        Used(MB)  Change"
echo "----------  --------  ------"
local prev_used=0
for log in $(ls -t "$LOG_DIR"/*_"$mount_safe".log | head -"$days"); do
date=$(basename "$log" | cut -d_ -f1)
used=$(tail -1 "$log" | awk '{print $2}')
used_mb=$((used / 1024))
if [ $prev_used -ne 0 ]; then
change=$((used_mb - prev_used))
printf "%s  %8d  %+6d\n" "$date" "$used_mb" "$change"
else
printf "%s  %8d  %6s\n" "$date" "$used_mb" "-"
fi
prev_used=$used_mb
done
}
# Setup cron job
# */5 * * * * /usr/local/bin/disk_logger.sh

9. Formatting and Output Control

Custom Output Formats

#!/bin/bash
# Pretty print with colors
color_df() {
df -h | awk '
BEGIN {
red = "\033[31m"
yellow = "\033[33m"
green = "\033[32m"
blue = "\033[34m"
reset = "\033[0m"
}
NR==1 {
print blue $0 reset
}
NR>1 {
gsub(/%/,"",$5)
if ($5 > 90) color = red
else if ($5 > 75) color = yellow
else color = green
$5 = $5"%"
printf "%s%s%s\n", color, $0, reset
}'
}
# CSV output
df_csv() {
df -h | awk '
BEGIN {print "filesystem,size,used,avail,use_pct,mount"}
NR>1 {
gsub(/%/,"",$5)
printf "%s,%s,%s,%s,%d,%s\n", $1, $2, $3, $4, $5, $6
}'
}
# JSON output
df_json() {
echo "{"
echo '  "timestamp": "'$(date -Iseconds)'",'
echo '  "filesystems": ['
df -h | tail -n +2 | awk '
BEGIN {sep=""}
{
gsub(/%/,"",$5)
printf "%s    {\n", sep
printf "      \"filesystem\": \"%s\",\n", $1
printf "      \"size\": \"%s\",\n", $2
printf "      \"used\": \"%s\",\n", $3
printf "      \"available\": \"%s\",\n", $4
printf "      \"use_percent\": %d,\n", $5
printf "      \"mount\": \"%s\"\n", $6
printf "    }"
sep=",\n"
}'
echo ""
echo '  ]'
echo "}"
}
# HTML report
df_html() {
cat << EOF
<html>
<head>
<title>Disk Usage Report</title>
<style>
body { font-family: Arial, sans-serif; }
table { border-collapse: collapse; width: 100%; }
th { background: #4CAF50; color: white; padding: 8px; }
td { border: 1px solid #ddd; padding: 8px; }
tr:nth-child(even) { background: #f2f2f2; }
.critical { background: #ff4444; color: white; }
.warning { background: #ffaa00; }
</style>
</head>
<body>
<h2>Disk Usage Report - $(date)</h2>
<table>
<tr>
<th>Filesystem</th>
<th>Size</th>
<th>Used</th>
<th>Avail</th>
<th>Use%</th>
<th>Mounted on</th>
</tr>
EOF
df -h | tail -n +2 | awk '
{
gsub(/%/,"",$5)
class = ""
if ($5 > 90) class = "critical"
else if ($5 > 75) class = "warning"
printf "        <tr class=\"%s\">\n", class
printf "            <td>%s</td>\n", $1
printf "            <td>%s</td>\n", $2
printf "            <td>%s</td>\n", $3
printf "            <td>%s</td>\n", $4
printf "            <td>%d%%</td>\n", $5
printf "            <td>%s</td>\n", $6
printf "        </tr>\n"
}'
cat << EOF
</table>
</body>
</html>
EOF
}
# Usage
# color_df
# df_csv > report.csv
# df_json > report.json
# df_html > report.html

10. Alerting and Notifications

Alert Scripts

#!/bin/bash
# Multi-channel alert system
CONFIG_FILE="/etc/disk_alert.conf"
ALERT_LOG="/var/log/disk_alerts.log"
# Default thresholds
WARN_THRESHOLD=80
CRIT_THRESHOLD=90
# Load configuration
if [ -f "$CONFIG_FILE" ]; then
source "$CONFIG_FILE"
fi
# Alert channels
send_email() {
local subject="$1"
local message="$2"
echo "$message" | mail -s "$subject" "${ALERT_EMAIL:[email protected]}"
}
send_slack() {
local message="$1"
local webhook="${SLACK_WEBHOOK:-}"
if [ -n "$webhook" ]; then
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"$message\"}" \
"$webhook" 2>/dev/null
fi
}
log_alert() {
local level="$1"
local message="$2"
echo "$(date -Iseconds) [$level] $message" >> "$ALERT_LOG"
}
# Check and alert
check_and_alert() {
local issues=0
df -h | tail -n +2 | while read line; do
use_pct=$(echo "$line" | awk '{print $5}' | tr -d '%')
mount=$(echo "$line" | awk '{print $6}')
filesystem=$(echo "$line" | awk '{print $1}')
if [ "$use_pct" -gt "$CRIT_THRESHOLD" ]; then
issues=$((issues + 1))
message="CRITICAL: $mount ($filesystem) is ${use_pct}% full"
log_alert "CRITICAL" "$message"
send_email "CRITICAL Disk Alert" "$message"
send_slack ":fire: $message"
elif [ "$use_pct" -gt "$WARN_THRESHOLD" ]; then
issues=$((issues + 1))
message="WARNING: $mount ($filesystem) is ${use_pct}% full"
log_alert "WARNING" "$message"
# Only email for warnings, maybe not Slack
send_email "Disk Warning" "$message"
fi
done
return $issues
}
# Escalation if issues persist
escalate_alert() {
local mount="$1"
local count_file="/tmp/alert_count_$(echo "$mount" | tr '/' '_')"
local max_count=3
if [ -f "$count_file" ]; then
count=$(cat "$count_file")
else
count=0
fi
count=$((count + 1))
echo "$count" > "$count_file"
if [ "$count" -ge "$max_count" ]; then
# Escalate - page on-call
send_email "ESCALATED: Disk alert for $mount" "Issue persisting"
# Could also send SMS, page, etc.
rm -f "$count_file"  # Reset after escalation
fi
}
# Main loop
while true; do
check_and_alert
sleep 300  # Check every 5 minutes
done

11. Troubleshooting

Common Issues and Solutions

#!/bin/bash
# Issue: "df: cannot read table of mounted filesystems"
# Solution: Check /etc/mtab or /proc/mounts
if [ ! -f /etc/mtab ]; then
echo "mtab missing, using /proc/mounts"
alias df='df -P'
fi
# Issue: Stale NFS mount causing hang
# Solution: Use timeout and exclude stale mounts
timeout_df() {
local timeout="${1:-10}"
timeout "$timeout" df -h 2>/dev/null || echo "df command timed out"
}
# Find stale NFS mounts
find_stale_nfs() {
mount -t nfs | awk '{print $3}' | while read mount; do
timeout 5 ls "$mount" >/dev/null 2>&1 || echo "Stale NFS: $mount"
done
}
# Issue: Permission denied for some mounts
# Solution: Use sudo for system mounts
sudo_df() {
sudo df -h "$@"
}
# Issue: Inconsistent sizes due to mounted filesystems
# Solution: Use --direct to avoid stat overhead (GNU)
df --direct -h
# Issue: Binary mount point names
# Solution: Use -P for POSIX format
df -P
# Debug df operations
debug_df() {
strace -e trace=file df -h 2>&1 | grep -E 'open|stat'
}

Diagnostic Tools

#!/bin/bash
# Comprehensive disk diagnostic
diagnose_disk() {
local mount="${1:-/}"
echo "=== Disk Diagnostics for $mount ==="
echo
echo "1. Basic filesystem info:"
df -h "$mount"
echo
echo "2. Filesystem type:"
df -T "$mount"
echo
echo "3. Inode usage:"
df -i "$mount"
echo
echo "4. Mount options:"
mount | grep " $mount "
echo
echo "5. Filesystem features:"
device=$(df "$mount" | tail -1 | awk '{print $1}')
if [ -b "$device" ]; then
tune2fs -l "$device" 2>/dev/null | head -10 || echo "Not an ext filesystem"
fi
echo
echo "6. Disk I/O stats:"
iostat -x 1 2 2>/dev/null | tail -20 || echo "iostat not available"
echo
echo "7. Top directories by size:"
du -sh "$mount"/* 2>/dev/null | sort -rh | head -10
echo
echo "8. Open files on this filesystem:"
lsof "$mount" 2>/dev/null | head -10 || echo "No open files or lsof not available"
}
# Check for disk errors
check_disk_errors() {
dmesg | grep -i "error\|i/o error\|disk full" | tail -20
}
# Validate filesystem
validate_fs() {
local mount="$1"
local device=$(df "$mount" | tail -1 | awk '{print $1}')
# Unmount first (if possible)
echo "This would check $device - requires unmount"
# sudo fsck -f "$device"
}

12. Best Practices and Tips

Shell Configuration

# ~/.bashrc additions
# Aliases for common df operations
alias df='df -h'                    # Human-readable by default
alias dfc='df -h --total'            # With total
alias dfl='df -h -l'                 # Local only
alias dfi='df -hi'                    # Inodes
alias dfext='df -h -t ext4 -t xfs'    # Only ext4 and xfs
alias dfhome='df -h /home'            # Home directory
alias dfroot='df -h /'                # Root filesystem
# Function to show usage with colors
dfcolor() {
df -h | awk '
BEGIN {
red="\033[31m"
yellow="\033[33m"
green="\033[32m"
blue="\033[34m"
reset="\033[0m"
}
NR==1 {print blue $0 reset; next}
{
gsub(/%/,"",$5)
color = ($5 > 90) ? red : ($5 > 75) ? yellow : green
printf "%s%s%s\n", color, $0, reset
}'
}
# Function to show top usage
dftop() {
local n="${1:-5}"
df -h | tail -n +2 | sort -k5 -rn | head -"$n"
}
# Function to monitor specific mount
dfwatch() {
local mount="${1:-/}"
local interval="${2:-5}"
watch -n "$interval" "df -h '$mount'"
}
# Complete with mount points
_df_complete() {
COMPREPLY=($(compgen -W "$(mount | awk '{print $3}')" -- "${COMP_WORDS[COMP_CWORD]}"))
}
complete -F _df_complete dfwatch dftop

System Administration Tips

#!/bin/bash
# 1. Regular monitoring setup (cron)
cat > /etc/cron.d/disk_monitor << 'EOF'
*/15 * * * * root /usr/local/bin/check_disk.sh >> /var/log/disk_check.log 2>&1
0 0 * * * root /usr/local/bin/disk_report.sh | mail -s "Daily Disk Report" [email protected]
EOF
# 2. Alert thresholds configuration
cat > /etc/disk_alert.conf << 'EOF'
WARN_THRESHOLD=80
CRIT_THRESHOLD=90
[email protected]
SLACK_WEBHOOK=https://hooks.slack.com/services/xxx/yyy/zzz
EXCLUDE_FS="tmpfs devtmpfs squashfs"
EOF
# 3. Automated cleanup when low on space
auto_cleanup() {
local mount="$1"
local threshold="${2:-90}"
usage=$(df -h "$mount" | tail -1 | awk '{print $5}' | tr -d '%')
if [ "$usage" -gt "$threshold" ]; then
echo "Disk usage critical on $mount, cleaning up..."
# Clean package manager cache
if [ -d /var/cache/apt ]; then
apt-get clean
fi
# Remove old logs
find /var/log -name "*.log" -mtime +30 -delete
find /var/log -name "*.gz" -mtime +30 -delete
# Remove old kernels (Ubuntu)
if command -v apt-get >/dev/null; then
apt-get autoremove --purge -y
fi
# Clear journal logs older than 7 days
journalctl --vacuum-time=7d 2>/dev/null
fi
}
# 4. Create alerts for trending
trend_alert() {
local mount="$1"
local log_file="/tmp/${mount}_trend.log"
# Record current usage
usage=$(df -h "$mount" | tail -1 | awk '{print $5}' | tr -d '%')
echo "$(date +%s) $usage" >> "$log_file"
# Keep last 30 days
tail -n 30 "$log_file" > "${log_file}.tmp"
mv "${log_file}.tmp" "$log_file"
# Calculate trend
if [ $(wc -l < "$log_file") -ge 7 ]; then
first=$(head -1 "$log_file" | awk '{print $2}')
last=$(tail -1 "$log_file" | awk '{print $2}')
if [ "$last" -gt "$first" ]; then
echo "Warning: $mount usage trending up ($first% -> $last%)"
fi
fi
}
# 5. Emergency actions
emergency_actions() {
local mount="$1"
# Find and kill processes using too much space
lsof "$mount" 2>/dev/null | awk '{print $2}' | sort -u | while read pid; do
if [ -d "/proc/$pid" ]; then
size=$(ls -l "/proc/$pid/fd" 2>/dev/null | grep -c "deleted")
if [ "$size" -gt 100 ]; then
echo "Process $pid has deleted files consuming space"
# kill -9 "$pid"  # Uncomment with caution
fi
fi
done
}

Conclusion

The df command is essential for system administration and disk space monitoring:

Key Takeaways

  1. Basic Usage: df shows filesystem disk usage
  2. Human-readable: -h for understandable sizes
  3. Filesystem Types: -T shows type, -t filters by type
  4. Inodes: -i shows inode usage
  5. Local Only: -l excludes network filesystems
  6. Total: --total shows sum of all
  7. Custom Output: Can be formatted with awk, sed
  8. Integration: Works great with other commands
  9. Monitoring: Essential for automated monitoring
  10. Troubleshooting: Helps identify disk issues

Best Practices Summary

  1. Always use -h for human-readable output
  2. Monitor regularly with cron jobs
  3. Set up alerts for high usage
  4. Check both space and inodes
  5. Exclude pseudo filesystems for clean reports
  6. Track trends for capacity planning
  7. Integrate with monitoring systems
  8. Document thresholds and actions
  9. Test cleanup procedures before emergencies
  10. Keep historical data for analysis

The df command, while simple, is crucial for maintaining system health and preventing outages due to full disks. Combined with other tools and proper monitoring, it forms the foundation of disk space management in Unix/Linux systems.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper