du 的核心原理:递归遍历 + block 计数
du 的本质是统计文件占用的磁盘块数量,而不是文件大小。这两者有微妙但重要的区别。
底层实现通过 stat() 系统调用获取每个文件的 st_blocks 字段:
// 简化版 du 实现核心逻辑
#include <sys/stat.h>
#include <dirent.h>
off_t calculate_usage(const char *path) {
struct stat st;
if (lstat(path, &st) < 0) return 0;
// 目录递归遍历
if (S_ISDIR(st.st_mode)) {
DIR *dir = opendir(path);
struct dirent *entry;
off_t total = 0;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 ||
strcmp(entry->d_name, "..") == 0) continue;
char fullpath[PATH_MAX];
snprintf(fullpath, sizeof(fullpath), "%s/%s", path, entry->d_name);
total += calculate_usage(fullpath);
}
closedir(dir);
return total + 512; // 目录本身占用 1 个 block
}
// 文件:返回块数 * 512 字节
return st.st_blocks * 512;
}
关键细节:
- st_blocks 是块数,不是字节数:实际占用 =
st_blocks * 512 - 稀疏文件处理:一个 10GB 的稀疏文件可能只占用几个块
- 目录本身占用:每个目录至少占用 4KB(一个 block)
常用参数详解
1. -h 人类可读格式 (Human-Readable Format)
du -h /var/log
# 4.0K /var/log/apt
# 12M /var/log/journal
# 128M /var/log
实现原理:遍历单位数组,找到最合适的单位:
function humanReadable(bytes: number): string {
const units = ['B', 'K', 'M', 'G', 'T', 'P']
let i = 0
while (bytes >= 1024 && i < units.length - 1) {
bytes /= 1024
i++
}
return `${bytes.toFixed(1)}${units[i]}`
}
2. -s 只显示总计
du -sh /home/user
# 2.5G /home/user
跳过递归输出,只返回顶层统计。
3. --max-depth 控制层级
du -h --max-depth=1 /var
# 4.0K /var/tmp
# 12M /var/log
# 128M /var/lib
# 145M /var
这对于快速定位大目录非常有用。
4. -a 显示所有文件
默认 du 只统计目录,-a 让它也输出每个文件:
du -ah /tmp
# 4.0K /tmp/test.txt
# 8.0K /tmp/data.json
# 12K /tmp
5. --exclude 排除特定文件
du -h --exclude="*.log" /var
du -h --exclude="node_modules" /home/user/project
支持通配符,可以多次使用排除多种模式。
实战场景与性能优化
场景 1:定位大目录
du -h --max-depth=1 /var | sort -hr
# 145M /var
# 128M /var/lib
# 12M /var/log
# 4.0K /var/tmp
sort -hr 按人类可读格式逆序排序,一眼看出最大的目录。
场景 2:排除特定目录
du -sh --exclude="node_modules" --exclude=".git" ~/projects/*
统计项目目录时排除依赖和版本控制目录,避免干扰。
场景 3:查找大文件
结合 find 使用:
find /var -type f -size +100M -exec du -h {} \;
或者直接用 du:
du -ah /var | grep -E "^[0-9.]+G"
场景 4:监控目录增长
watch -n 60 "du -sh /var/log"
每分钟检查 /var/log 的空间占用,适合监控日志增长。
性能考量
1. 大目录遍历优化
du 遍历整个目录树,对于百万文件的目录会很慢。几个优化思路:
- –one-file-system:不跨文件系统,避免遍历挂载点
- –threshold:只显示超过指定大小的目录,减少输出
- 并行处理:GNU parallel 并行统计子目录
# 并行统计子目录
find /var -maxdepth 1 -type d | parallel du -sh
2. 硬链接处理
du 默认会多次计数硬链接。使用 -l 参数只计数一次:
du -lh /path/to/directory
3. 缓存问题
频繁运行 du 会影响文件系统缓存。可以用 nocache 命令避免:
nocache du -sh /large/directory
与 df 的区别
很多人混淆 du 和 df:
- df:文件系统级别的空间统计,读取 superblock
- du:目录/文件级别的空间统计,遍历目录树
典型差异场景:
df -h /var
# Filesystem Size Used Avail Use% Mounted on
# /dev/sda1 50G 45G 5.0G 90% /
du -sh /var
# 35G /var
为什么 df 显示 45G,du 只有 35G?
- 已删除但仍被占用的文件:进程打开的文件被删除后,空间不会立即释放
- 预留空间:ext4 默认预留 5% 给 root
- 元数据开销:inode 表、journal 等
查找已删除但仍被占用的文件:
lsof +L1 | grep deleted
Web 实现:浏览器端目录统计
通过 File System Access API,可以在浏览器中实现类似功能:
async function calculateDirectorySize(dirHandle: FileSystemDirectoryHandle): Promise<number> {
let totalSize = 0
for await (const entry of dirHandle.values()) {
if (entry.kind === 'file') {
const file = await entry.getFile()
totalSize += file.size
} else if (entry.kind === 'directory') {
const subDirHandle = await dirHandle.getDirectoryHandle(entry.name)
totalSize += await calculateDirectorySize(subDirHandle)
}
}
return totalSize
}
// 使用示例
const dirHandle = await window.showDirectoryPicker()
const size = await calculateDirectorySize(dirHandle)
console.log(`Total size: ${formatBytes(size)}`)
注意:浏览器 API 统计的是文件大小,不是磁盘块数,与原生 du 有差异。
常见陷阱
1. 权限不足
du 会跳过无权限的目录,统计结果可能不完整:
du: cannot read directory '/root': Permission denied
使用 sudo 获取完整统计。
2. 符号链接
默认 du 不跟随符号链接。使用 -L 参数跟随:
du -Lh /path/to/symlink
3. 稀疏文件误判
# 创建稀疏文件
dd if=/dev/zero of=sparse.img bs=1 count=0 seek=10G
ls -lh sparse.img
# -rw-r--r-- 1 user user 10G May 9 22:00 sparse.img
du -h sparse.img
# 0 sparse.img # 实际占用为 0
ls 显示文件大小,du 显示磁盘占用。
总结
du 命令虽然简单,但背后的设计蕴含了 Unix 哲学:专注做一件事,并把它做好。从 block 计数到递归遍历,从硬链接处理到稀疏文件支持,每个细节都经过精心设计。
下次遇到磁盘空间问题,不妨试试这些组合:
# 快速定位大目录
du -h --max-depth=1 | sort -hr | head -10
# 排除干扰项
du -sh --exclude="node_modules" --exclude=".git" *
# 监控目录增长
watch -n 60 "du -sh /var/log"
希望这篇对你理解 du 命令有帮助。更详细的命令参数可以查看: Linux du 命令参考
相关工具:Linux df 磁盘空间监控 | 文件大小转换器
转载自CSDN-专业IT技术社区



