1. 从一次磁盘告警说起:为什么你需要掌握du命令
那天下午,我正在调试一个服务,突然收到监控系统的告警邮件:“服务器/data/logs目录磁盘使用率超过 90%”。这可不是小事,日志爆满轻则导致服务无法写入新日志,重则可能直接写满根分区,让整个系统宕机。我立刻 SSH 连上去,第一反应就是看看这个目录下到底是谁在“搞鬼”。是某个服务的日志文件失控滚动了?还是临时文件没有及时清理?如果只是简单地用ls -lh,你只能看到当前目录下直接子文件的大小,对于嵌套很深的目录结构,这无异于盲人摸象。你需要一个能“透视”整个目录树,并精准定位空间占用元凶的工具。在 Linux 的世界里,这个工具就是du(Disk Usage)。
对于任何一位 Linux 系统管理员、运维工程师甚至是后端开发者来说,du命令都是工具箱里最基础也最不可或缺的一把瑞士军刀。它解决的问题非常直接:快速、准确地评估文件和目录对磁盘空间的消耗情况。无论是日常的磁盘空间巡检、定位异常增长的文件,还是清理工作前的“摸底调查”,du都能提供关键数据。本文将带你深入du命令的方方面面,不仅告诉你如何查看指定目录下各个文件大小及总体大小,更会分享我在十多年运维生涯中积累的实战技巧和避坑指南,让你下次面对磁盘空间告警时,能从容应对,一击即中。
2.du命令核心解析:参数、输出与工作原理
在深入实战之前,我们必须先理解du命令的基本语法和它到底在“算”什么。du命令的常规格式是du [选项] [文件或目录...]。如果不指定文件或目录,它会默认分析当前目录。
2.1 最常用的核心选项
让我们先看几个你几乎每次都会用到的选项:
-h(human-readable):这是必选项。它会把以字节为单位的原始数字,转换成人类易读的格式(K、M、G、T)。对比一下:# 不使用 -h $ du /var/log 4 /var/log/private 8 /var/log/installer 205832 /var/log # 使用 -h $ du -h /var/log 4.0K /var/log/private 8.0K /var/log/installer 202M /var/log显然,看到
202M比看到205832要直观得多。这里的M代表 Mebibyte (MiB),在二进制体系中,1 MiB = 1024 KiB,1 KiB = 1024 Bytes。这是 Linux 工具的标准做法。-s(summarize):只显示指定目录的总大小,而不列出其内部每一个子项。当你只关心一个目录整体占用了多少空间时,这个选项非常高效。$ du -sh /home 47G /home一行输出,干净利落地告诉你
/home目录总共用了 47G。-c(total):在最后一行产生一个总计。当同时查看多个目录时,这个总计很有用。它常与-s联用。$ du -sch /var/log /var/cache 202M /var/log 154M /var/cache 356M total--max-depth=N:控制命令遍历目录的深度。这是定位问题的关键参数。--max-depth=0:等同于-s,只显示目标目录本身的总大小。--max-depth=1:显示目标目录及其直接子目录(或文件)的大小。这是最常用的深度,可以快速看到一级子项的空间分布。
$ du -h --max-depth=1 /var 154M /var/cache 0 /var/lock 0 /var/mail 202M /var/log ... (其他目录) 1.2G /var
2.2du与df的差异:一个经典的“空间去哪了”之谜
在相关热词里,我看到了centos du df 磁盘空间差太多。这确实是一个高频困惑点。du(Disk Usage) 和df(Disk Free) 统计的是不同的东西。
df:报告文件系统的磁盘空间使用情况。它读取的是文件系统超级块(superblock)中的元数据,统计的是已被分配(used)和未被分配(free)的数据块。这些数据块可能正在被文件使用,也可能被已删除但仍被进程占用的文件占用,或者干脆就是文件系统的预留空间、元数据区。du:通过遍历目录树,累加每个文件所占用的数据块数量,然后乘以块大小(通常是 4KiB)来计算空间。
为什么会出现差异?
- 已删除但未释放的文件:如果一个文件被删除(
rm),但仍有进程正在打开它(例如,一个日志文件被服务进程写入后又被误删),df会显示空间仍被占用,因为数据块尚未释放;但du遍历不到这个已删除的文件名,所以不会计入。这是最常见的原因。可以用lsof | grep deleted命令查找这类文件。 - 文件系统预留空间:Ext3/Ext4 等文件系统默认会预留 5% 的空间给 root 用户,以防普通用户写满磁盘导致系统无法运行。这部分空间
df会算在Used里,但du不会统计。 - 稀疏文件 (Sparse Files):像虚拟机磁盘镜像(
.qcow2,.vmdk)或数据库文件,可能声明了一个很大的逻辑大小,但实际只占用少量物理块。du默认报告物理占用(-b或--apparent-size可以看逻辑大小),而ls -l看到的是逻辑大小,这也会造成认知差异。 - 文件系统元数据(如 Journal):日志文件系统(如 Ext4)的日志区占用空间,
df会计入,du不计。
所以,当du和df结果对不上时,不要慌张,这通常是正常的。你需要结合lsof和df -i(查看 inode 使用情况)等命令进行综合判断。
3. 实战:定位目录空间占用与排序分析
现在,我们回到开头的那个告警场景:/data/logs目录快满了。我们该如何一步步定位问题?
3.1 第一步:查看目录总体大小与一级子项分布
首先,我们得知道总共有多少“存货”,以及大致是哪些“品类”占了大头。
$ du -sh /data/logs 89G /data/logs $ du -h --max-depth=1 /data/logs 12K /data/logs/nginx 4.0G /data/logs/app_a 82G /data/logs/app_b 1.2G /data/logs/app_c 89G /data/logs一目了然,app_b这个服务的日志目录是绝对的“空间杀手”,占了 82G。问题范围立刻从 89G 缩小到了 82G。
3.2 第二步:深入“问题目录”,按大小排序
接下来,我们进入app_b目录,并希望看到里面最大的文件或子目录。这里就需要结合sort命令了。du本身不排序,默认输出顺序依赖于文件系统的读取顺序(通常按 inode 编号或文件名)。
# 进入目录,查看所有文件和子目录的大小(深度为1),并按人类可读的数字逆序排序 $ cd /data/logs/app_b $ du -h --max-depth=1 | sort -hr 82G . 76G ./application.log.20231015 4.1G ./application.log 987M ./debug.log.20231014 ... (其他较小文件)sort -hr是关键:
-h:让sort能正确理解人类可读的数值(如 82G, 4.1G),而不是进行字符串排序。-r:逆序,最大的排在最前面。
踩坑提示:如果你在 MacOS 的终端里操作,BSD 版本的sort可能不支持-h参数。一个替代方案是使用-n(数字排序),但前提是du不能使用-h输出。你可以这样做:du -k --max-depth=1 | sort -nr。-k选项让du以 KiB 为单位输出,这样就是纯数字,可以用-n排序。不过,这样可读性会差一些。
3.3 第三步:处理已滚动的历史日志文件
从排序结果看,一个名为application.log.20231015的历史日志文件独占 76G。这很可能是因为日志切割工具(如logrotate)配置了按日期切割,但忘记配置或错误配置了删除旧日志的策略(maxage,maxsize)。
此时,你需要检查logrotate的配置文件(通常在/etc/logrotate.d/下),确认对于app_b的日志,是否有类似maxsize 100M,daily,rotate 7(保留最近7天)这样的配置。如果没有,就需要添加并手动触发一次logrotate,或者直接安全地删除这些过期文件。
安全删除建议:对于确定要删除的大文件,不要直接用rm,尤其是当磁盘空间非常紧张时。因为rm只是删除文件名链接,如果文件正被进程打开,空间并不会立即释放。更好的做法是使用truncate或:>命令清空文件内容,或者用echo > filename,这样能立即释放磁盘空间。确认服务不受影响后,再安排重启相关服务以彻底删除文件句柄,或者直接rm。
# 清空文件内容(文件大小变为0,空间立即释放) $ truncate -s 0 /data/logs/app_b/application.log.20231015 # 或者 $ :> /data/logs/app_b/application.log.202310154. 高级技巧与场景化应用
掌握了基础操作,我们来看看一些更高效、更贴合复杂场景的用法。
4.1 排除特定文件或目录
有时目录里包含一些你明知很大但又不想在统计中看到的文件,比如虚拟机镜像、Docker 容器数据卷或者特定的缓存目录。du本身没有内置的排除模式,但我们可以结合find命令来实现。
例如,我想统计/home目录大小,但排除所有.cache目录和.iso文件:
$ find /home -type f ! -name "*.iso" ! -path "*/.cache/*" -exec du -ch {} + | tail -1这个命令有点复杂,它先用find找到所有非.iso文件且路径中不包含/.cache/的文件,然后交给du -c计算,最后用tail -1取总计行。但注意,这种方法对于排除目录并不完美,因为find仍然会进入.cache目录去匹配文件,可能会影响性能。
一个更直观的方法是使用--exclude参数(注意:这是 GNUdu的扩展,并非所有系统都支持,但在大多数 Linux 发行版上可用):
$ du -sh --exclude=".cache" --exclude="*.iso" /home这个命令就清晰多了,它会忽略所有名为.cache的目录和所有以.iso结尾的文件。
4.2 统计特定类型文件的总大小
开发中常需要知道某个项目里所有图片、日志或者编译产出(如.o,.class)占了多少空间。
# 统计当前目录及子目录下所有 `.log` 文件的总大小 $ find . -name "*.log" -type f -exec du -ch {} + | tail -1 # 统计所有 `.jpg` 和 `.png` 图片的总大小 $ find . \( -name "*.jpg" -o -name "*.png" \) -type f -exec du -ch {} + | tail -14.3 与awk结合进行自动化分析与告警
在脚本中,我们可能需要设定阈值进行自动化检查。awk是处理du文本输出的利器。
#!/bin/bash TARGET_DIR="/data/logs" THRESHOLD_GB=50 # 获取目录大小(以字节为单位),并转换为GB size_gb=$(du -sb "$TARGET_DIR" | awk '{print $1/1024/1024/1024}') # 使用bc进行浮点数比较 if (( $(echo "$size_gb > $THRESHOLD_GB" | bc -l) )); then echo "警告:目录 $TARGET_DIR 大小为 ${size_gb}GB,已超过阈值 ${THRESHOLD_GB}GB。" # 可以在这里添加发送邮件、钉钉消息等告警逻辑 # 同时,可以自动运行分析命令,将最大的10个文件列出来附在告警信息里 echo "空间占用最大的10个文件/目录:" du -ah "$TARGET_DIR" | sort -rh | head -11 # head -11 因为第一行是总计 fi这个脚本定期运行(比如放入 crontab),就能实现磁盘空间的主动监控。
4.4 处理包含大量文件的目录
如果你对一个包含数十万甚至上百万文件的目录(比如邮件队列、图片存储目录)直接运行du,它可能会运行非常久,因为需要为每个文件执行stat系统调用。一个优化技巧是使用du的--files0-from参数(如果支持)或者利用find的-print0和xargs -0来更高效地处理。
另外,对于海量小文件,文件系统本身的元数据(inode)也可能被耗尽,此时df -i会显示 inode 使用率 100%,即使df -h显示还有空间。这也是du和df差异的一个潜在原因。
5. 图形化替代方案与可视化工具
虽然命令行高效,但在向非技术同事汇报或者需要更直观展示时,图形化工具很有帮助。
ncdu(NCurses Disk Usage):这是一个基于文本界面的交互式du工具。它会在终端里绘制一个可导航的界面,让你用键盘方向键浏览目录树,按大小排序,甚至可以直接删除文件。安装简单(apt install ncdu或yum install ncdu),是命令行爱好者的首选可视化工具。$ ncdu /data/logs运行后,你会看到一个清晰的层级结构,按大小降序排列,交互体验极佳。
baobab(Disk Usage Analyzer):如果你在使用 GNOME 桌面环境,baobab提供了一个完整的图形化界面,用树状图和旭日图(sunburst)直观地展示磁盘使用情况。它同样支持扫描远程目录(通过 GVfs),功能强大。gdmap:另一个图形化工具,使用树状图展示,视觉效果不错。
对于日常的快速分析和问题定位,我个人的习惯是:首选ncdu。它既保留了命令行的效率,又提供了图形化的直观,无需离开终端,在远程服务器上也能完美工作。