Tested script

Alert when a filesystem is nearly full (Bash)

Checks every real filesystem and prints an alert for any at or above a usage threshold. It prints nothing when all is well, so it suits cron, which emails you only when there is output.

Tested

The script

#!/usr/bin/env bash
# disk-usage-alert.sh: warn when any filesystem is fuller than a threshold.
#
# Usage: disk-usage-alert.sh [-t PERCENT] [MOUNTPOINT...]
#   -t PERCENT   alert when use is at or above this (default 90)
#   MOUNTPOINT   only check these; default is every real filesystem
#
# Exit codes: 0 all fine, 1 at least one over the threshold, 2 bad usage.
# Read-only. Suitable for cron: it prints nothing when everything is fine.
set -euo pipefail

usage() { sed -n '4,8p' "$0" | sed 's/^# \{0,1\}//'; }

threshold=90
while getopts ':t:h' opt; do
  case "$opt" in
    t) threshold="$OPTARG" ;;
    h) usage; exit 0 ;;
    *) usage >&2; exit 2 ;;
  esac
done
shift $((OPTIND - 1))

if ! [[ "$threshold" =~ ^[0-9]+$ ]] || (( threshold > 100 )); then
  echo "Error: threshold must be a whole number from 0 to 100, got '$threshold'" >&2
  exit 2
fi

# -P keeps each filesystem on one line. Skip RAM-backed, squashfs and CD/DVD
# (iso9660) mounts: optical media always shows 100% full.
# df exits 1 if even one mount cannot be read, so only give up if nothing came back
df_output=$(df -P -x tmpfs -x devtmpfs -x squashfs -x overlay -x iso9660 "$@") || true
if [[ $(printf '%s\n' "$df_output" | wc -l) -lt 2 ]]; then
  echo "Error: df returned no filesystems to check" >&2
  exit 2
fi

alerts=0
while read -r fs _size _used _avail pct mount; do
  use=${pct%\%}
  [[ "$use" =~ ^[0-9]+$ ]] || continue
  if (( use >= threshold )); then
    echo "ALERT: $mount ($fs) is ${use}% full (threshold ${threshold}%)"
    alerts=$((alerts + 1))
  fi
done < <(printf '%s\n' "$df_output" | tail -n +2)

(( alerts == 0 )) || exit 1

Run it

./disk-usage-alert.sh
./disk-usage-alert.sh -t 80 / /home

How it works

Options

getopts reads -t for the threshold and -h for help. Any other arguments are mount points to check. The help text is the comment block at the top, printed by sed, so the two never drift apart.

Validate

A regular expression checks the threshold is a whole number, and an arithmetic test checks it is 100 or less. Bad input exits with code 2.

Read df

df -P uses the POSIX format, which keeps each filesystem on one line even when the device name is long. The -x options skip RAM-backed filesystems like tmpfs, snap images (squashfs), container layers and CD or DVD mounts, which are always 100% full. df exits with an error if even one mount cannot be read, so the script only gives up if nothing came back at all.

Check each line

tail -n +2 drops the header. read splits each line into fields, and ${pct%\%} strips the percent sign. Any filesystem at or above the threshold gets an ALERT line and the counter goes up.

Exit code

0 means all fine, 1 means at least one alert, 2 means bad usage or no filesystems. Monitoring tools can use the code without parsing the text.