Jalankan du -sh pada sebuah folder lalu bandingkan dengan ukuran di Finder, dan hasilnya sering berbeda, kadang sampai beberapa gigabyte. Tidak ada yang salah. Keduanya menjawab pertanyaan yang berbeda: berapa panjang file-nya, dan berapa banyak ruang disk yang ditempatinya. Saat disk penuh, hanya yang kedua yang penting, dan menghitungnya dengan benar butuh lebih dari sekadar menjumlahkan st_size.
Aturan di bawah ini adalah yang diikuti layar Disk pada aplikasi desktop osFoundry. Scanner-nya adalah fungsi Rust pendek di atas std::fs, dan logika yang sama muat dalam sekitar 40 baris Python. Semua angka berasal dari folder uji di volume APFS dengan blok alokasi 4 KB.
Hitung blok, bukan byte
st_size adalah panjang file. st_blocks adalah jumlah satuan 512 byte yang dialokasikan sistem file untuknya, berapa pun ukuran blok sebenarnya dari volume. Scanner di aplikasi memakai yang kedua secara default:
fn allocated_bytes(meta: &fs::Metadata, apparent: bool) -> u64 { if apparent { return meta.len(); } #[cfg(unix)] { use std::os::unix::fs::MetadataExt; meta.blocks().saturating_mul(512) } #[cfg(not(unix))] { meta.len() } } Tiga file di folder uji menunjukkan alasannya:
| File | st_size | st_blocks × 512 |
|---|---|---|
small.txt (2 byte teks) | 2 | 4,096 |
sparse.img (dipotong jadi 1 GiB, tidak pernah ditulisi) | 1,073,741,824 | 0 |
big.bin (200 MiB data) | 209,715,200 | 209,715,200 |
File sekecil apa pun tetap memakai satu blok penuh, sehingga folder berisi 100.000 file kecil di disk bisa beberapa ratus megabyte lebih besar daripada jumlah byte-nya. File sparse kebalikannya: disk VM, file database, dan beberapa unduhan memesan panjang yang besar tetapi hanya memakai blok untuk bagian yang sudah ditulis. du di macOS menghitung blok; du -A menampilkan ukuran semu (apparent size). Kotak Apparent size di aplikasi melakukan peralihan yang sama.
Jangan ikuti link, jangan keluar dari volume
Scanner yang mengikuti symbolic link bisa menghitung folder yang sama dua kali, atau berputar tanpa henti pada link yang menunjuk ke folder induknya sendiri. Scanner yang masuk ke drive yang di-mount akan menambahkan file dari disk lain ke total disk ini. Aplikasi membaca tiap entri dengan symlink_metadata, yang menggambarkan link itu sendiri, bukan targetnya, dan membandingkan ID perangkat dengan folder tempat pemindaian dimulai:
if child_meta.file_type().is_symlink() || !same_device(root_meta, &child_meta) { continue; } same_device membandingkan st_dev, yaitu pemeriksaan yang sama seperti du -x atau find -xdev.
Hard link dan clone
Hard link adalah satu file dengan beberapa nama. Di folder uji, setelah ln big.bin hard.bin, ls -l menampilkan dua file 200 MiB, tetapi du menghitung datanya sekali saja karena ia mengingat setiap pasangan (st_dev, st_ino) yang pernah dilihat ketika st_nlink di atas 1. Scanner apa pun yang menjumlahkan blok seharusnya melakukan hal yang sama. Cadangan Time Machine pada disk HFS+ dan beberapa package manager banyak memakai hard link.
Clone APFS lebih sulit. cp -c big.bin clone.bin (atau Duplicate di Finder) membuat file kedua yang berbagi blok dengan aslinya sampai salah satunya berubah. Tiap file punya inode sendiri, st_blocks sendiri, dan jumlah link 1, sehingga du menghitung 400 MiB untuk pasangan itu. Diukur dengan df sebelum dan sesudah, clone memakai 0 KB ruang kosong, sedangkan cp biasa untuk file yang sama memakai 204.984 KB. Tidak ada field POSIX yang menunjukkan pembagian blok ini, jadi total berbasis blok melebih-lebihkan ruang yang kembali saat clone dihapus. Untuk folder penuh proyek duplikat atau pustaka foto, df sebelum dan sesudah adalah satu-satunya pemeriksaan yang bisa diandalkan.
Versi Python
Aturan yang sama, hanya dengan pustaka standar:
import os import stat import sys def usage(root): root_dev = os.lstat(root).st_dev seen = set() allocated = apparent = 0 skipped = 0 stack = [root] while stack: path = stack.pop() try: entries = list(os.scandir(path)) except OSError: skipped += 1 continue for e in entries: try: st = e.stat(follow_symlinks=False) except OSError: skipped += 1 continue if stat.S_ISLNK(st.st_mode) or st.st_dev != root_dev: continue if stat.S_ISDIR(st.st_mode): stack.append(e.path) continue if st.st_nlink > 1: key = (st.st_dev, st.st_ino) if key in seen: continue seen.add(key) allocated += st.st_blocks * 512 apparent += st.st_size return allocated, apparent, skipped if __name__ == "__main__": alloc, appar, skipped = usage(sys.argv[1]) print(f"on disk {alloc / 2**30:8.2f} GiB") print(f"apparent {appar / 2**30:8.2f} GiB") if skipped: print(f"skipped {skipped} unreadable entries") Pada folder uji, skrip ini mencetak 0,39 GiB di disk dan 1,39 GiB apparent, sama dengan du -sk (409.604 KB) dan du -sAk. Pada pohon source code 14 GiB, hasilnya sama dengan du -sk sampai dua angka desimal. Simpan jumlah entri yang dilewati: di macOS, folder yang dilindungi izin privasi (Mail, Messages, Safari) tidak bisa dibaca tanpa Full Disk Access, dan total yang diam-diam melewatkannya terlihat lebih lengkap daripada kenyataannya.
Aplikasi menjaga hasilnya cukup kecil untuk digambar. Setiap folder menyimpan 36 anak terbesarnya, dan sisanya dijumlahkan menjadi satu node "Other items", sehingga membuka folder node_modules dengan seribu paket menampilkan 36 kotak dan satu kotak lagi untuk sisanya, bukan seribu kepingan kecil.
Menghapus dengan aman
Risiko sebenarnya dari alat disk ada di langkah menghapus. Aplikasi memindahkan item ke Trash sistem dengan crate trash, bukan menghapusnya dengan unlink, dan memeriksa setiap path sebelum memindahkan apa pun:
if !target.starts_with(&root) || target == root || home.as_ref() == Some(&target) { return Err("Cannot move a scan root or home folder to Trash".into()); } if mount_points .iter() .any(|mount| target == *mount || target.parent() == Some(mount.as_path())) { return Err("Cannot move a volume or its top-level items to Trash".into()); } Path dikanonikalisasi lebih dulu, jadi .. atau symbolic link tidak bisa menyelipkan target di luar folder yang dipindai. Pilihan yang berisi satu folder bertanda di dalam folder bertanda lainnya juga ditolak, karena begitu folder induk masuk Trash, path anaknya tidak ada lagi dan batch akan gagal di tengah jalan.
Aplikasi desktop ini gratis di halaman unduh osFoundry.



