一個檔案的兩道門
檔名走了空間還沒回來
你把一個 8 GB 的錄影暫存檔丟進垃圾桶,檔案列表立刻少了一行;儲存空間卻沒有跟著回來。幾分鐘後,正在產生錄影的程式還能繼續寫入,直到它結束,空間才突然釋放。這個現象不適用於每一個作業系統,但在 POSIX/Unix 類檔案系統裡,它有一個很準確的答案。
直接答案:檔名不是檔案內容本身。Unix 類系統把資料與目錄裡的名稱分開;刪掉名稱只移除一條可尋址的路徑。只要程序還握著已開啟的檔案描述元,資料仍有一個有效引用,儲存空間通常要等最後一個名稱連結與最後一個開啟引用都消失後,才可以回收。Windows 也有自己的檔案分享、刪除與關閉規則,不能把這條 POSIX 路徑的每個細節直接套過去。
往下讀,看見「不在資料夾裡」和「不再占空間」之間的差別
01/先拆開檔案
門牌不是房間
POSIX 把 unlink() 定義成移除一個目錄項目,不是立刻把所有內容擦掉。檔案系統在資料夾裡留下一個名字,名字再指向實際的資料;Finder 或終端機列出的是名字,儲存空間計算的則是資料區塊。兩個畫面都在談同一份檔案,背後卻不是同一個物件。
當一個程序用 open() 開啟檔案時,作業系統會回傳一個檔案描述元。這個描述元是程序之後讀寫檔案的握把;它指向已開啟的檔案描述,而不是每次操作都重新依賴原本的路徑。路徑之後被移除,已經拿到握把的程序仍然可以繼續使用它;只是新的程序不能再用那個消失的名字找到同一份資料。
meeting-capture.mov 提供可尋找的入口file descriptor 保留已開啟的引用02/看見引用
剪掉門牌,握把還在
同一份資料會經過三個容易混淆的狀態。請切換按鈕,看見門牌、握把與資料區如何分開;捲動這段場景時,畫面也會依序從「有名字」走向「名字消失」再走向「最後放手」。
停用 JavaScript 時固定展示「名稱已移除」:資料夾看不到門牌,但程序握把仍抓住資料,空間還沒有回來;正文完整說明三個狀態與硬連結限制。
名稱已移除:資料夾少了一行,但程序握把仍抓住資料,8 GB 仍在占用。
捲動進度:門牌正在離開名冊,資料區還沒有放手。
03/不是故障
程式有時會故意先丟掉名字
許多 Unix 程式會先建立並開啟暫存檔,再立刻移除它的目錄名稱,只把檔案描述元留在自己手上。這樣做可以讓其他程序無法用檔名碰到半成品,也不用另外清理一個可能過期的暫存名字。程序繼續讀寫自己的握把;等它關閉檔案,最後一個引用才會離開。
這個模式也能解釋為什麼某些下載暫存檔、錄影中間檔或服務程式的日誌,看起來已經從資料夾消失,儲存空間卻還在被占用。那不一定是刪除失敗,而可能是程式仍在用一份「沒有門牌、但還有人握著」的檔案。不同程式和檔案系統會選擇不同做法,不能只從檔名是否出現在列表裡猜測狀態。
04/邊界會改變答案
真正的最後一個握把
硬連結:門牌不只一張
硬連結會讓同一份資料擁有不只一個目錄名稱。刪掉其中一個名字,並不會讓其他名字失效;即使所有名字都消失,只要還有程序開著檔案,資料也仍然有引用。POSIX 的條件很清楚:連結數量變成零,而且沒有程序再開著檔案,檔案占用的空間才可以被釋放。
Windows:有自己的門鎖
Windows 對開啟中檔案的刪除則有自己的分享模式與 delete-on-close 規則。某些情況會因為分享設定而拒絕刪除,某些情況會先標記為關閉後刪除;這和 macOS/Linux 常見的 unlink 體驗不完全相同。因此,本文的「剪掉門牌、保留握把」是 POSIX/Unix 類路徑的解釋,不是 Windows 使用者遇到所有刪除問題時的通用答案。
05/把握把找出來
找到仍握住檔案的程式
在 macOS 或 Linux 上,如果一個大檔已經沒有目錄名稱,卻可能仍被程序開著,可以先用 lsof +L1 查詢。lsof 會列出程序開啟中的檔案;+L1 會把連結數小於 1、也就是已移除名稱但仍開啟的檔案挑出來。輸出通常會包含程序名稱、PID、檔案描述元與原本的路徑,足以讓你回頭判斷是哪個程式還沒有放手。
lsof +L1- 找到程序後,先讓它正常結束、關閉檔案或完成目前的寫入,再重新查看可用空間。
- 不要只因為看到一個 PID 就直接強制終止;如果那個程序仍在寫入,硬切可能造成資料不完整。
- lsof 可能因權限看不到其他使用者的程序;查不到結果不代表所有空間問題都一定是同一個原因。
06/回到那個 8 GB 檔案
刪除有兩道門
回到那個 8 GB 的錄影暫存檔。你把它丟進垃圾桶時,第一道門是門牌被剪掉:資料夾不再列出它。第二道門要等錄影程式關閉檔案握把:最後一個引用消失後,檔案系統才有機會回收那 8 GB。空間晚一點才回來,不是垃圾桶突然改變心意,而是兩道門本來就不是同一道門。
所以,刪掉大檔卻沒有釋放空間時,先問的不是「還要不要再刪一個」,而是「誰還握著它?」macOS/Linux 可以從 lsof +L1 開始查;Windows 則要依它自己的開啟控制代碼與檔案分享規則處理。看得見的檔名只是入口,真正決定資料何時離場的,是最後一個仍存在的引用。