前提条件:

sudo apt updatesudo apt install -y vnstat jqsudo systemctl enable --now vnstatsudo vnstat --versionsudo vnstat --iflistsudo vnstat --add -i eth0     # 初始化接口数据库(2.x)sudo systemctl enable --now vnstatsudo systemctl restart vnstatvnstat -i eth0 --days

检测脚本:

#!/usr/bin/env bash# v2.9.eth0.monthly.800GiB# 功能:使用 vnStat 读取“本自然月(month[0])”的出站(tx)字节,总量 >= 800 GiB 时关机。# 依赖:vnstat, jq, systemd (用于关机与服务管理)# 用法:作为 systemd oneshot 服务由定时器每分钟/每N分钟调用一次,或手动执行测试。# 注意:需以 root 运行。set -euo pipefail### ======================== 可 配 置 区 =========================IFACE="eth0"              # 监控的网卡THRESHOLD_GIB=800         # 本月出站阈值(GiB,1024^3)LOG_TAG="tx-quota"        # syslog 标签AUTO_INSTALL=true         # 若未安装 vnstat/jq,是否自动安装(基于 apt)SHUTDOWN_CMD="/usr/bin/systemctl poweroff"  # 触发动作(可改为 halt/reboot)### ============================================================THRESHOLD_BYTES=$(( THRESHOLD_GIB * 1024 * 1024 * 1024 ))require_root() {  if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then    echo "This script must run as root." >&2    exit 1  fi}have_cmd() { command -v "\$1" >/dev/null 2>&1; }auto_install_deps() {  local missing=()  have_cmd vnstat || missing+=("vnstat")  have_cmd jq     || missing+=("jq")  if (( ${#missing[@]} > 0 )); then    if [[ "$AUTO_INSTALL" == "true" ]]; then      if have_cmd apt-get; then        echo "Installing dependencies: ${missing[*]} ..."        apt-get update -y        apt-get install -y "${missing[@]}"      else        echo "Missing deps: ${missing[*]} and apt-get not found. Install them manually." >&2        exit 1      fi    else      echo "Missing deps: ${missing[*]}. Set AUTO_INSTALL=true or install manually." >&2      exit 1    fi  fi}ensure_vnstat_running_and_iface() {  # 确保 vnstat 服务运行  if have_cmd systemctl; then    systemctl enable --now vnstat.service >/dev/null 2>&1 || true  fi  # 确保接口已被 vnstat 追踪(若数据库不存在则初始化)  # vnstat -u -i 不会破坏已有数据;仅在缺失时创建数据库  vnstat -u -i "$IFACE" >/dev/null 2>&1 || true  # 尝试读取一次,若失败则再重启服务后重试  if ! vnstat --json m -i "$IFACE" >/dev/null 2>&1; then    have_cmd systemctl && systemctl restart vnstat.service >/dev/null 2>&1 || true    # 再给数据库一点时间(通常不需要;保险起见)    sleep 1  fi}get_monthly_tx_bytes() {  # 取“本月”的 tx(bytes)。vnStat JSON 结构:interfaces[0].traffic.month[0].tx  # 某些刚初始化的环境可能 month[0] 不存在 -> 用 // 0 兜底  local json tx  set +e  json=$(vnstat --json m -i "$IFACE" 2>/dev/null)  local rc=$?  set -e  if (( rc != 0 )) || [[ -z "$json" ]]; then    echo 0    return  fi  tx=$(echo "$json" | jq -r '.interfaces[0].traffic.month[0].tx // 0' 2>/dev/null || echo 0)  [[ "$tx" =~ ^[0-9]+$ ]] || tx=0  echo "$tx"}main() {  require_root  auto_install_deps  ensure_vnstat_running_and_iface  # 读取本月出站总量(bytes)  local tx_bytes  tx_bytes="$( get_monthly_tx_bytes )"  # 记录日志并判断  if (( tx_bytes >= THRESHOLD_BYTES )); then    logger -t "$LOG_TAG" "Monthly outbound on ${IFACE}: ${tx_bytes} >= ${THRESHOLD_BYTES}; shutting down."    echo "Quota exceeded: ${tx_bytes} >= ${THRESHOLD_BYTES}. Powering off..."    # 防止重复触发:如果你用的是 systemd 定时器,关机会终止后续调用;无需额外去重。    $SHUTDOWN_CMD  else    logger -t "$LOG_TAG" "Monthly outbound on ${IFACE}: ${tx_bytes}/${THRESHOLD_BYTES}; ok."    echo "OK: ${tx_bytes}/${THRESHOLD_BYTES} bytes used this month on ${IFACE}."  fi}main "$@"

配置服务:

# /etc/systemd/system/shutdown-on-monthly-tx-quota.service[Unit]Description=Shutdown when monthly outbound on eth0 exceeds 800GiB (vnStat)[Service]Type=oneshotExecStart=/usr/local/sbin/shutdown_on_monthly_tx_quota.sh

配置定时:

# /etc/systemd/system/shutdown-on-monthly-tx-quota.timer[Unit]Description=Run monthly outbound quota checker every 10 minutes[Timer]OnBootSec=10minOnUnitActiveSec=10minUnit=shutdown-on-monthly-tx-quota.service[Install]WantedBy=timers.target

定时启动:

sudo systemctl daemon-reloadsudo systemctl enable --now shutdown-on-monthly-tx-quota.timer