马春杰杰 Exit Reader Mode

如何动态调整Linux中某个目录的大小?

这里使用了exf4prjquota的特性。

假如想要调整的目录路径为:/disk/Individual-limits/machunjie

那么,只需要以下步骤即可:

mkdir -p /disk/Individual-limits/machunjie

echo "machunjie:2002" >> /etc/projid

echo "2002:/disk/Individual-limits/machunjie" >> /etc/projects

xfs_quota -x -f -c "project -s machunjie" /disk

xfs_quota -x -f -c "limit -p bsoft=280G bhard=300G machunjie" /disk

xfs_quota -x -f -c "report -p" /disk

可以看到结果:

root@autodl:/disk/Individual-limits/machunjie# df -hT /disk/Individual-limits/machunjie
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/sdc1      ext4  280G  4.0K  280G   1% /disk

有几点需要注意:

1、2002是身份ID,可以随意设置,只要与其他目录不一致就行

2、需要先确认你的挂载点/disk是否开启prjquota特性

3、这里设置了软限制和硬限制,区别:
Soft limit:超过这个值时,系统会开始发出警告,但在宽限期(grace period)内仍允许写入。
Hard limit:绝对上限,超过就直接报 Disk quota exceeded,无法再写入。

自动化脚本

下面提供一个自动化脚本,可以方便的创建磁盘限额,使用的时候,以 root 运行,把挂载点(默认 /disk)改成你的实际挂载点即可。

脚本名称:set_project_quota.sh

#!/usr/bin/env bash
set -euo pipefail

# Usage:
#   set_project_quota.sh <name> <path> <soft> <hard> [--mount /disk] [--id 2001] [--grace 7days]
#
# Examples:
#   set_project_quota.sh lijiaqing /disk/Individual-limits/lijiaqing 0 200G --mount /disk
#   set_project_quota.sh machunjie /disk/Individual-limits/machunjie 280G 300G --mount /disk --id 2002 --grace 3days

NAME="${1:-}"; shift || true
PATH_DIR="${1:-}"; shift || true
SOFT="${1:-}"; shift || true
HARD="${1:-}"; shift || true

MOUNT_POINT="/disk"
EXPLICIT_ID=""
GRACE=""

# ---- Parse optional flags ----
while [[ $# -gt 0 ]]; do
  case "$1" in
    --mount) MOUNT_POINT="${2:-}"; shift 2;;
    --id)    EXPLICIT_ID="${2:-}"; shift 2;;
    --grace) GRACE="${2:-}"; shift 2;;
    *) echo "Unknown arg: $1"; exit 1;;
  esac
done

if [[ -z "${NAME}" || -z "${PATH_DIR}" || -z "${SOFT}" || -z "${HARD}" ]]; then
  echo "Usage: set_project_quota.sh <name> <path> <soft> <hard> [--mount /disk] [--id 2001] [--grace 7days]"
  exit 1
fi

command -v xfs_quota >/dev/null 2>&1 || { echo "xfs_quota not found. Install xfsprogs."; exit 1; }

# ---- Basic checks ----
[[ -d "${MOUNT_POINT}" ]] || { echo "Mount point ${MOUNT_POINT} not found."; exit 1; }
mount | grep -qE " on ${MOUNT_POINT} " || { echo "${MOUNT_POINT} is not a mount point."; exit 1; }
mount | grep -E " on ${MOUNT_POINT} " | grep -q prjquota || {
  echo "WARNING: ${MOUNT_POINT} not mounted with prjquota. Please enable 'prjquota' and remount."; exit 1;
}

mkdir -p "${PATH_DIR}"

# ---- Ensure /etc/projid and /etc/projects exist ----
touch /etc/projid /etc/projects

# ---- Get or assign Project ID ----
get_id_by_name() {
  awk -F: -v n="$1" '$1==n{print $2}' /etc/projid | tail -n1
}
get_path_by_id() {
  awk -F: -v i="$1" '$1==i{print $2}' /etc/projects | tail -n1
}

PID="$(get_id_by_name "${NAME}")"
if [[ -n "${EXPLICIT_ID}" ]]; then
  PID="${EXPLICIT_ID}"
fi

if [[ -z "${PID}" ]]; then
  # auto-generate numeric ID from name, avoid collisions
  # start from checksum; if occupied, add 1 until free
  BASE=$(echo -n "${NAME}" | cksum | awk '{print $1}')
  PID="${BASE}"
  while grep -qE "^${PID}:" /etc/projects || grep -qE "^.*:${PID}$" /etc/projid; do
    PID=$((PID + 1))
  done
fi

# ---- Wire name<->id and id<->path (idempotent updates) ----
# /etc/projid:   NAME:PID
if grep -qE "^${NAME}:" /etc/projid; then
  # update existing name mapping
  sed -i -E "s|^(${NAME}:).*|\1${PID}|" /etc/projid
else
  echo "${NAME}:${PID}" >> /etc/projid
fi

# /etc/projects: PID:/path
if grep -qE "^${PID}:" /etc/projects; then
  # update path for this PID
  sed -i -E "s|^(${PID}:).*|\1${PATH_DIR}|" /etc/projects
else
  echo "${PID}:${PATH_DIR}" >> /etc/projects
fi

# ---- Initialize project on filesystem ----
xfs_quota -x -f -c "project -s ${NAME}" "${MOUNT_POINT}"

# ---- Set limits ----
# SOFT=0 means clear soft limit
if [[ "${SOFT}" == "0" || "${SOFT}" == "none" || "${SOFT}" == "NONE" ]]; then
  xfs_quota -x -f -c "limit -p bsoft=0 bhard=${HARD} ${NAME}" "${MOUNT_POINT}"
else
  xfs_quota -x -f -c "limit -p bsoft=${SOFT} bhard=${HARD} ${NAME}" "${MOUNT_POINT}"
fi

# ---- Set grace (optional, only matters if bsoft>0) ----
if [[ -n "${GRACE}" && "${SOFT}" != "0" && "${SOFT}" != "none" && "${SOFT}" != "NONE" ]]; then
  xfs_quota -x -f -c "timer -p bsoft=${GRACE}" "${MOUNT_POINT}" || true
fi

# ---- Show result ----
echo "==== Quota report for ${NAME} (PID ${PID}) on ${MOUNT_POINT} ===="
xfs_quota -x -f -c "report -p" "${MOUNT_POINT}" | awk -v n="${NAME}" 'NR==1,NR==3{print; next} $1==n{print; seen=1} END{if(!seen) print "Not found in report (unexpected)."}'
echo "Done."

命令:

1. 给 machunjie 新建目录并设置 300G 硬限额(不设软限额):

bash set_project_quota.sh machunjie /disk/Individual-limits/machunjie 0 300G --mount /disk --id 2002

1. 给 machunjie 新建目录并设置 180G软限额300G 硬限额

bash set_project_quota.sh machunjie /disk/Individual-limits/machunjiee 180G 200G --mount /disk