包包baobaolin.com
date
entry
037
topic
tooling
rev
—

macOS 上 df / 顯示 82%,APFS 容器其實已用掉 99.4%

df / 說 82%,同一時間 APFS 容器只剩約 2.7 GiB。根目錄掛的是一個封印的系統快照,百分比的分子只有它自己的用量。

檢查磁碟還剩多少,最順手的是 df -h /,看 Capacity 那一欄。如果工作資料和 / 在同一個檔案系統上,這樣看沒什麼問題。

但 2026 年 10 月 13 日下午,我在這台 Mac 上看到 df -h / 顯示 82%,同一時間它所在的 APFS 容器只剩約 2.7 GiB。這篇要談的是 macOS 上 df / 的百分比在算什麼:先看兩行 df,再看 / 實際掛的是什麼、Capacity 怎麼算,最後是其他工具看到的數字和我現在怎麼檢查。

同一時間的兩行 df

macOS 26.4.1,2026-10-13 06:37:19 UTC(台灣時間 14:37)。以下主要讀值都在這一刻前後連續量的,較早的讀值和推算另外標出:

$ df -h / /System/Volumes/Data
Filesystem        Size    Used   Avail Capacity iused ifree %iused  Mounted on
/dev/disk3s1s1   460Gi    12Gi   2.7Gi    82%    458k   29M    2%   /
/dev/disk3s5     460Gi   429Gi   2.7Gi   100%    6.1M   29M   18%   /System/Volumes/Data

兩行的 Size 和 Avail 一樣,Used 差了約 417.7 GiB,Capacity 一個 82%、一個 100%(Data 的 100% 是進位後的顯示,當時仍有約 2.7 GiB 可用)。同一天稍早(06:05 UTC 左右),/ 那一行是 Avail 3.3Gi、Capacity 78%。約半小時內可用空間少了 0.6 GiB 左右,/ 的百分比只多了 4 個百分點。

/ 是一個封印的系統快照

diskutil apfs list disk3 把這個容器拆開來看(依原始輸出整理,省略部分欄位):

Container disk3
  Size (Capacity Ceiling):      494384795648 B (494.4 GB)
  Capacity In Use By Volumes:   491455111168 B (491.5 GB) (99.4% used)
  Capacity Not Allocated:       2929684480 B (2.9 GB) (0.6% free)
  System    Capacity Consumed: 12.6 GB   Snapshot Mount Point: /   Snapshot Sealed: Yes
  Preboot   10.8 GB
  Recovery   2.6 GB
  Data     461.1 GB
  VM         4.3 GB

同一個 APFS 容器裡的卷共用同一池空間;這個容器裡有 System、Preboot、Recovery、Data、VM 這幾個主要的卷(另有一個掛在 /System/Volumes/Update)。掛在 / 的是系統卷的封印快照(disk3s1s1,mount 顯示 sealed, read-only),System 卷的 Capacity Consumed 是 12.6 GB,df 的 Used 顯示 12 GiB。這台機器的家目錄和 /tmp 都在 Data 卷上。

Apple Platform Security 指南寫,macOS 10.15 起系統內容放在獨立的唯讀系統卷,macOS 11 起再加上簽章(signed system volume),用的是 APFS 快照。所以在同一個開機快照下,/ 的 Used 通常是穩定的;其他卷的寫入改變的是大家共用的 Avail。

Capacity 是 Used ÷ (Used + Avail),再進位

man df 在 APFS 的範例底下說明:同一個分割區上的虛擬卷,大小和可用空間都一樣,只有使用量不同;Capacity 欄是各檔案系統的使用量占「使用量加可用空間」的百分比。這次量的兩個卷也是這樣。

代進 df -k 的數字:/ 是 12,258,500 ÷ (12,258,500 + 2,861,024),約 81.08%,df 印 82%;Data 卷是 450,260,592 ÷ (450,260,592 + 2,861,024),約 99.37%,印 100%。兩個都是無條件進位到整數。分母是「這一卷的 Used 加上共用的 Avail」,分子只有這一卷自己。

所以 / 的比率要到 90%,Avail 得掉到 Used 的九分之一,約 1.3 GiB;要到 95%,約 0.6 GiB。因為顯示值會進位,「顯示 90%」會早一點,在 Avail 低於約 1.44 GiB 時就出現。假設有一條監控規則是「df / 的 Capacity 大於 90% 就通知」,在 Used 不變的情況下,它要等 Avail 低於約 1.3 GiB 才會觸發;這個 460 GiB 容器的一成是 46 GiB,它要等到剩不到三十分之一才響(我沒有這樣的監控,這是照公式算的)。

其他工具看到的是另一個數字

同一時間,兩個常見的 API 對 / 回的值(GiB,取一位小數):

$ python3 -c 'import shutil; u=shutil.disk_usage("/"); print(*(round(x/2**30,1) for x in u))'
460.4 457.7 2.7
$ node -e 'const s=require("fs").statfsSync("/"); console.log((s.bavail*s.bsize/2**30).toFixed(1))'
2.7

Python 的 shutil.disk_usage 在 Unix 上,used 是 (f_blocks − f_bfree) × f_frsize,free 是 f_bavail × f_frsize。這裡 used 是 457.7 GiB,457.7 ÷ 460.4 約 99.4%,取一位小數時和容器的使用率相符,不是 df 那一欄單一卷的 12 GiB。Node 的 fs.statfsSync(v18.15.0、v19.6.0 加入)用 bavail × bsize 算可用空間,也是 2.7 GiB。這次量測裡,各工具的可用空間取到一位小數都是 2.7 GiB,差別在「用了多少」怎麼定義,以及由它算出來的百分比。

我現在怎麼看剩多少

在 macOS 上檢查磁碟,我改成看 Avail 的絕對值,不看百分比。Data 卷的百分比也不等於容器的使用率(這次只是剛好都接近 99.4%);要看容器整體,用 diskutil。工作開始前的檢查長這樣,df 失敗或取不到數字都要停:

#!/bin/sh
df_output=$(LC_ALL=C df -k /System/Volumes/Data) || {
  echo "無法取得 Data 卷的可用空間" >&2; exit 1; }
avail_kib=$(printf '%s\n' "$df_output" | awk 'NR==2 {print $4}')
case "$avail_kib" in
  ''|*[!0-9]*) echo "df 回傳的可用空間不是整數" >&2; exit 1 ;;
esac
# 剩不到 5 GiB 就停下來(依工作預估的寫入量調整)
if [ "$avail_kib" -lt $((5 * 1024 * 1024)) ]; then
  echo "磁碟剩 $((avail_kib / 1024)) MiB,先清理再跑" >&2
  exit 1
fi

這是開工前的一次檢查,不是持續監控。要找是誰吃掉空間,先用 diskutil apfs list 看各卷的 Capacity Consumed,再對 Data 卷底下的目錄做 du;du 數不到快照保留的空間,也會受 APFS clone 共用區塊影響,加總不一定對得上容器。這天機器上有好幾個工作在跑,空間下降可能和它們的暫存檔有關,但哪一個寫了多少我沒有量。

如果只記得一件事

在這台 Mac 上,df / 的百分比不能代表 APFS 容器用掉多少。開頭那個 82%,分子是系統快照的 12 GiB,分母是這 12 GiB 加上整個容器共用的剩餘空間;同一時間容器已用 99.4%。看剩多少就看 Avail,看容器整體就用 diskutil。

修訂紀錄

  1. 首次發布