1. 项目概述:为什么“du”命令是Linux运维的“听诊器”
在Linux系统管理的日常里,磁盘空间告急的红色警报,恐怕是每个运维工程师和开发者都经历过的“心跳时刻”。服务器响应变慢、应用无法写入日志、甚至数据库直接挂掉,追根溯源,往往是一块被日志文件、缓存数据或者临时文件塞满的磁盘。这时候,一个高效、精准的磁盘空间分析工具,就成了排查问题的“听诊器”。而du(Disk Usage)命令,正是这把听诊器中最核心、最常用的探头。
你可能用过df -h看一眼磁盘使用率,但它只告诉你文件系统整体的“水位”,却无法定位到底是哪个目录、哪个“坏小子”文件在疯狂吞噬空间。du命令的价值就在于“钻探”,它能深入到文件系统的每一个角落,统计出目录和文件的实际磁盘占用,让你对存储消耗了如指掌。无论是清理陈年日志、归档历史数据,还是评估应用存储需求、规划扩容方案,du都是你命令行工具箱里不可或缺的一把利器。
网上教程很多,但大多只罗列几个常用参数。我想结合自己多年在服务器上“救火”和“巡检”的经验,不仅带你掌握du的命令语法,更重点分享如何组合使用这些参数,形成高效的问题定位工作流,以及那些手册里不会写、但实践中血泪换来的“避坑指南”。比如,为什么du和df显示的总空间有时对不上?如何快速找出占用空间最大的前10个目录?面对数百万个小文件,du命令慢如蜗牛怎么办?这些实战问题,我们都会一一拆解。
2. 核心需求解析:我们到底需要du做什么?
在深入命令细节之前,我们先明确使用du的几个核心场景和需求。理解这些,你才能在不同的情况下选择最合适的“武器组合”。
2.1 快速定位“空间黑洞”
这是du最经典的应用。当/或/home分区使用率超过90%时,你需要快速回答:“哪个目录占用了最多空间?” 一个简单的du -sh /*可以给你根目录下所有一级子目录的大小概览,但面对嵌套很深的目录结构,你需要更智能的“钻取”能力,快速定位到问题源头,比如是/var/log下的日志轮转失效,还是/tmp下的临时文件堆积如山。
2.2 评估目录大小,为数据迁移或备份提供依据
在进行服务器迁移、数据备份或归档前,你必须清楚知道目标目录的实际数据量。这不仅关系到备份窗口的时间估算,更直接影响到你需要准备多大的目标存储空间。du -s提供的总计大小,是制定这些操作计划的关键输入。
2.3 监控与趋势分析
在自动化运维中,du常被嵌入监控脚本,定期统计特定目录(如应用日志目录、用户家目录)的增长情况。通过对比历史数据,可以提前预警存储风险,甚至分析出业务量的增长趋势。例如,监控/var/www/uploads目录的日增长量,可以间接反映用户活跃度。
2.4 解决“du”与“df”的统计差异之谜
这是一个经典困惑:df显示磁盘用了80%,但用du逐级统计所有文件的总和,可能只有70%。那10%的空间“消失”去哪了?这通常涉及已删除但未被释放的文件(被进程占用)、文件系统预留空间、或稀疏文件(sparse file)等概念。理解du的统计原理,是解开这个谜团的第一步。
3. 命令语法精讲与核心参数拆解
du命令的基本格式是du [选项] [文件或目录...]。如果不指定文件或目录,则默认为当前目录。它的核心原理是递归地遍历指定路径下的所有文件和目录,统计每个条目占用的磁盘块(block)数量,默认以512字节或1K字节块为单位显示。下面我们拆解最常用、最关键的几个参数。
3.1 基础显示参数:-h, -s, -c
-h(human-readable):这是使用频率最高的参数,没有之一。它将以人类易读的形式(K, M, G, T)显示大小,而不是默认的磁盘块数。du -h /var/log一眼就能看出是几百兆还是几个G,极大提升了可读性。-s(summarize):总计模式,只显示指定目录的总大小,而不显示其内部每个子项。当你只关心一个目录整体占用了多少空间时,用它。例如du -sh /home直接告诉你/home分区下所有用户数据的总量。-c(total):在最后一行产生一个总计。当同时查看多个目录时特别有用。比如du -shc /var/log /var/cache会分别显示两个目录的大小,并在最后一行显示它们的总和。
一个经典组合:du -sh。-s和-h的结合,是快速查看任何一个目录总大小的标准姿势。我几乎养成了条件反射,看到任何目录都想用du -sh点一下。
3.2 深度控制参数:--max-depth
这是进行高效空间分析的关键武器。它控制du命令遍历目录的深度。
--max-depth=N:指定显示到第 N 级子目录的统计信息。N=0 时,效果等同于-s。- 实战场景:
- 快速扫描一级目录:
du -h --max-depth=1 /。这能立即告诉你根目录下哪些一级子目录是“大户”,通常是定位问题的第一步。 - 逐级深入:发现
/var很大后,执行du -h --max-depth=1 /var,看到是/var/log大;再执行du -h --max-depth=1 /var/log,如此层层递进,像侦探破案一样精准定位。
- 快速扫描一级目录:
注意:
--max-depth参数在有些老版本或特定发行版的du中可能写作-d。例如在 macOS 的 BSD 版本中,就使用-d N。在 Linux 上,通常两者都支持,但--max-depth是更标准的 GNU 选项。
3.3 排除与筛选参数:--exclude 与 ! 模式
当目录中包含你明确不想统计的内容时(比如巨大的镜像文件.iso,或版本控制目录.git),排除功能就至关重要。
--exclude=PATTERN:排除匹配 PATTERN 的文件或目录。PATTERN 支持 shell 的通配符。du -sh --exclude='*.log' /var:统计/var时排除所有.log文件。du -sh --exclude='./cache' --exclude='./tmp' .:排除当前目录下的cache和tmp目录。
- 结合
find进行复杂筛选:对于更复杂的排除逻辑(如按文件时间、类型),du本身能力有限。这时可以联合find命令。例如,只统计最近7天修改过的文件大小:
这个命令先由find /path/to/dir -type f -mtime -7 -exec du -ch {} + | tail -1find找出7天内的文件,然后交给du统计并汇总。
3.4 排序与定位“最大”文件/目录
du本身不排序,但结合sort和head命令,就能瞬间找出占用空间最大的项。这是清理磁盘空间时的杀手锏。
经典命令链:找出当前目录下最大的10个子目录
du -h --max-depth=1 | sort -hr | head -n 10du -h --max-depth=1:列出当前目录下一级子目录的大小(含当前目录本身)。sort -hr:-h参数让sort能正确识别人类可读的大小单位(K, M, G),-r表示逆序(从大到小)。head -n 10:只显示前10行。
进阶技巧:如果想排除.(当前目录总计)行,可以稍作调整:
du -h --max-depth=1 | grep -P '^[0-9\.]+[KMGTP]?\t' | sort -hr | head -n 10这里用grep过滤出以数字和单位开头的行,去掉了以.\t开头的当前目录总计行。
4. 高级应用场景与性能优化实战
掌握了基础参数,我们来看看在复杂场景下如何运用,并解决du命令本身可能遇到的性能问题。
4.1 场景一:精准统计特定类型文件的总大小
假设你需要统计一个项目目录下所有.jpg图片的总占用空间。
方法A:使用find+du(推荐)
find /path/to/project -name "*.jpg" -type f -exec du -ch {} + | grep total$find ... -exec du -ch {} +:find找到所有.jpg文件,然后一次性传递给du -c统计,-h方便阅读。最后的grep total$只提取总计行。- 优点:相对高效,
-exec ... +会尽量合并参数,减少du进程的启动次数。
方法B:使用find+awk(适用于极大量文件)
find /path/to/project -name "*.jpg" -type f -printf "%s\n" | awk '{sum+=$1} END {print sum}'-printf "%s\n":直接输出每个文件的字节大小。awk进行累加。- 优点:完全避免启动外部命令
du,在文件数量巨大时(几十万以上)速度优势明显。最后得到的是字节数,可以再用numfmt或自己计算转换成易读格式。
4.2 场景二:处理海量小文件导致的du命令缓慢问题
du命令慢,主要慢在磁盘I/O和遍历文件节点(inode)上。当目录中包含数百万个小文件时(例如邮件存储、文档缓存),du可能会卡住很久。
优化策略1:使用--apparent-size参数
du --apparent-size统计的是文件的“逻辑大小”,即ls -l看到的大小,而不是实际占用的“磁盘块”大小。对于大量小文件,计算逻辑大小比计算磁盘块分布要快,因为它不需要查询文件系统块分配图。- 代价:结果不准确,尤其对于稀疏文件或文件系统有压缩/去重功能时,逻辑大小远大于实际磁盘占用。仅用于快速估算和比较相对大小时可考虑。
优化策略2:在文件系统层面使用xfs_io或btrfs工具
- 对于 XFS 文件系统,可以使用
xfs_io -c "stat"来快速获取目录的块信息,但这不是通用方案。 - 对于 Btrfs 文件系统,可以使用
btrfs filesystem du -s来获取快照感知的磁盘使用情况,速度很快。
优化策略3:终极方案——改变数据存储模式
- 如果业务上允许,将海量小文件打包成
tar归档或SquashFS等只读镜像,可以极大减少文件系统元数据开销,不仅节省空间,也让后续的du统计变得飞快。这需要从应用架构上考虑。
4.3 场景三:解析 “du” 与 “df” 的输出差异
这是运维面试常见题,也是实际排查中的高频困惑点。假设执行df -h /data显示已用空间 80G,但du -sh /data统计总和只有 70G。那10G去哪了?
主要原因有以下几点:
- 已删除文件但被进程占用:这是最常见的原因。如果一个进程打开了一个大文件(比如日志文件),即使你从文件系统中用
rm删除了它,只要进程未关闭文件句柄,该文件占用的磁盘空间就不会释放。df认为空间已被占用,而du因为文件已不可见,所以不统计它。- 排查命令:
lsof | grep deleted。这条命令可以列出所有已被删除但仍被进程打开的文件。重启相关进程或清空文件(如echo "" > /path/to/file.log)即可释放空间。
- 排查命令:
- 文件系统预留空间:Ext3/Ext4 等文件系统默认会保留约5%的空间给 root 用户,防止普通用户写满磁盘导致系统无法运行。这部分空间
df会算在已用或保留里,而du不会统计。 - 文件系统元数据(Journal, inode tables等):
df报告的是整个块设备的使用情况,包括日志、超级块、inode表等元数据占用的空间。du只统计用户文件数据。 - 稀疏文件(Sparse File):像虚拟机磁盘镜像(
.qcow2,.vdi)或数据库快照,可能声明了一个很大的逻辑大小,但实际只分配了写入数据的块。du --apparent-size看到的是逻辑大小(大),而du默认看到的是实际分配的块大小(小)。df反映的是实际分配。
理解要点:df从文件系统全局视角看块分配;du从用户文件视角看数据内容。两者统计维度不同,存在差异是正常的。当差异巨大时,首先怀疑“已删除但被占用的文件”。
5. 脚本化与自动化监控实战
将du命令嵌入脚本,可以实现自动化的磁盘空间监控和清理。
5.1 简单的目录大小监控脚本
下面是一个bash脚本示例,它检查指定目录是否超过阈值,并通过邮件报警。
#!/bin/bash # 文件名:check_disk_usage.sh TARGET_DIR="/var/log" THRESHOLD_GB=50 # 阈值,单位GB EMAIL="admin@example.com" # 使用du获取目录大小(以KB为单位),然后转换为GB USAGE_KB=$(du -sk "$TARGET_DIR" | cut -f1) USAGE_GB=$((USAGE_KB / 1024 / 1024)) # 将KB转换为GB if [ $USAGE_GB -gt $THRESHOLD_GB ]; then SUBJECT="警报:目录 $TARGET_DIR 占用空间超过 ${THRESHOLD_GB}GB" BODY="当前目录大小:${USAGE_GB}GB。\n请立即登录服务器检查并清理。\n\n占用最大的10个子目录:\n$(du -h --max-depth=1 "$TARGET_DIR" | sort -hr | head -n 11)" echo -e "$BODY" | mail -s "$SUBJECT" "$EMAIL" echo "$(date): 警报已发送。目录大小 ${USAGE_GB}GB。" >> /var/log/disk_monitor.log fi脚本要点解析:
du -sk以KB为单位获取总计,便于后续计算。cut -f1只提取大小数字部分。- 通过数学计算
$(( ... ))将KB转换为GB。 - 报警邮件正文中,直接附上了占用最大的子目录列表,方便接收者第一时间定位问题。
5.2 结合cron实现定期清理
对于已知的会不断增长的目录(如应用日志、临时文件),可以设置定时任务定期清理。
# 编辑crontab: crontab -e # 每天凌晨3点,清理超过30天的日志文件,并记录操作 0 3 * * * find /var/log/myapp -name "*.log" -mtime +30 -delete >> /var/log/log_cleanup.log 2>&1 # 每周一凌晨2点,统计并报告/var目录的大小变化 0 2 * * 1 /usr/local/bin/report_var_usage.shreport_var_usage.sh脚本内容示例:
#!/bin/bash USAGE=$(du -sh /var | cut -f1) echo "$(date): /var 目录总使用量: $USAGE" >> /var/log/var_usage_trend.log # 可以在这里添加更复杂的逻辑,比如与上周数据对比,增长过快则报警6. 常见问题排查与避坑指南
在实际使用中,你肯定会遇到各种奇怪的现象。这里记录了几个我踩过的坑和解决方案。
6.1 问题:du命令在某个目录下执行卡住,长时间无响应
- 可能原因1:目录中存在无法访问的挂载点或损坏的符号链接。
- 排查:使用
ls -la查看目录内容,检查是否有异常的挂载点(如挂载了已断开的NFS)或指向不存在位置的符号链接。 - 解决:对于挂载点,尝试
umount或修复网络连接。对于坏链接,可以删除或修复。使用du -x可以避免跨越文件系统边界,有时能绕过挂载点问题。
- 排查:使用
- 可能原因2:目录层级极深或文件数量巨大。
- 排查:使用
find /path -type f | wc -l粗略估计文件数量。 - 解决:
- 使用
timeout命令限制执行时间:timeout 30s du -sh /path。 - 尝试使用
--apparent-size进行快速估算。 - 考虑使用更底层的工具,如
ls -lR结合awk脚本,但这对大量文件同样慢。 - 根本解决:优化存储结构,合并小文件。
- 使用
- 排查:使用
6.2 问题:du -h显示的大小单位混乱,排序 (sort -h) 不正确
- 可能原因:语言环境(Locale)设置导致
du -h使用了非标准的千位分隔符(如逗号)或单位符号。sort -h可能无法正确解析这些格式。 - 解决:在执行命令前强制设置语言环境为C(标准英文),确保输出格式一致。
将LC_ALL=C du -h --max-depth=1 | LC_ALL=C sort -hrLC_ALL=C放在命令前,可以临时改变该命令的执行环境,使数字和单位格式标准化。
6.3 问题:统计网络文件系统(NFS)目录时速度极慢
- 原因:
du需要在客户端遍历所有文件并逐个向NFS服务器发起属性查询(getattr)请求,网络延迟被放大。 - 优化建议:
- 在NFS服务器端执行:如果可能,登录到NFS服务器上,对导出的源目录执行
du命令。 - 使用
-x参数:确保du不跨越文件系统,但这对NFS本身帮助有限。 - 服务器端启用
rpc.statd缓存:优化NFS属性缓存,但这需要管理员权限配置服务器。 - 考虑替代方案:对于只读的NFS共享,可以定期在服务器端生成目录大小的清单文件,客户端通过读取这个清单文件来获取信息。
- 在NFS服务器端执行:如果可能,登录到NFS服务器上,对导出的源目录执行
6.4 一个容易被忽略的参数:--time
- 用途:
du --time可以显示文件或目录的最后修改时间。这在排查“最近哪个目录突然变大”的问题时非常有用。 - 组合使用:
du -h --time --max-depth=1 /data | sort -hr。这样你不仅能看到大小,还能看到最近修改时间,结合判断,能更快定位到活跃的“空间消耗者”。
最后,关于工具的选择,ncdu(NCurses Disk Usage) 是一个基于文本界面的交互式du工具,它可视化地展示目录大小,并允许你通过键盘导航和删除文件,对于不熟悉命令行的用户或进行手动清理时非常直观高效。但在自动化脚本和远程SSH会话中,原生的du命令因其纯粹和可脚本化,依然拥有不可替代的地位。
掌握du,不仅仅是记住几个参数,更是建立起一套从全局扫描到精准定位,从手动排查到自动监控的磁盘空间管理方法论。下次再面对磁盘空间告警时,希望你能从容地拿起这套组合工具,快速找到问题的根源。