1. 从“我的硬盘去哪儿了?”说起:du与sort的黄金搭档
不知道你有没有遇到过这种情况:服务器或者自己的开发机,用着用着突然就弹出“磁盘空间不足”的警告。看着满屏的文件和目录,你根本不知道是哪个“大胃王”偷偷吃掉了宝贵的空间。这时候,你可能会本能地打开文件管理器,试图手动一个个文件夹去查看属性,但在一个动辄包含成千上万个文件的Linux服务器上,这无异于大海捞针。
几年前,我刚接手一个线上服务时,就踩过这个坑。报警邮件半夜响起,提示某台应用服务器磁盘使用率超过95%。登录上去一看,/根目录下几十个文件夹,/home、/var、/opt…… 每个下面又套着无数子目录。当时我对Linux命令还不熟,情急之下差点想重启服务器碰碰运气。幸好一位前辈提醒:“别慌,先用du看看哪个目录最大,再用sort排个序,一眼就能找到罪魁祸首。” 我照做了,命令很简单:du -sh /* | sort -hr。结果立马显示/var/log目录占用了近80%的空间,进去一看,原来是一个被遗忘的调试服务产生了海量的日志文件,且没有配置日志轮转。问题瞬间定位,清理日志后空间立刻释放。
自那以后,du(disk usage)和sort这两个命令就成了我日常运维和开发工作中的“黄金搭档”。它们单独使用已经很强大了,但组合起来,简直就是分析和清理磁盘空间的“手术刀”和“显微镜”。du负责精准测量每个目录的“体重”,而sort则负责把这些体重数据按照从胖到瘦(或从瘦到胖)的顺序排列好,让你一眼锁定目标。无论你是系统管理员、后端开发者,还是数据工程师,只要你的工作环境涉及Linux,这套组合拳就是你必须掌握的生存技能。它不要求你有多高深的编程知识,却能解决实实在在的、影响系统稳定性的核心问题。
2. du命令详解:不只是看看大小那么简单
很多人对du命令的理解停留在du -sh(查看当前目录总大小)这个层面,这就像只学会了汽车的启动和刹车,远未发挥其全部性能。du命令的真正威力在于其丰富的选项,可以让你从不同维度、不同深度去洞察磁盘空间的分布情况。
2.1 核心选项拆解与使用场景
du命令的选项很多,但最常用、最核心的就那么几个。理解它们,你就能应对绝大多数场景。
-h(human-readable):这是必选项这是让du命令变得“友好”的关键。没有它,du输出的数字单位是千字节(KiB),对于动辄几个G甚至几个T的现代存储来说,一堆数字很难直观理解。加上-h后,du会自动转换为K、M、G、T等人类可读的单位。例如,4096会变成4.0K,10485760会变成10M。我个人的习惯是,几乎在所有情况下都加上-h,除非在做需要精确字节数的脚本处理。
-s(summarize):查看目录总览这个选项让du只显示指定目录的总大小,而不深入列出其内部每一个子项。这是快速评估一个目录是否“肥胖”的最快方式。比如,你想知道/home用户目录总共占了多大空间,就用du -sh /home。如果不用-s,它会递归列出/home下每个用户目录,每个用户目录下每个文件夹…… 输出会又长又难以阅读。
-d(max-depth):控制探测深度这是进行“分层诊断”的利器。-d后面跟一个数字,表示深入到第几级子目录。例如,在根目录/下执行du -h -d 1,它会显示/下所有一级子目录(如/bin,/home,/var)的大小。这比-s更细致,又比无深度限制的递归更清晰。
- 场景对比:
du -sh /var:只知道/var总大小。du -h -d 1 /var:可以知道是/var/log大,还是/var/cache大,还是/var/lib大。du -h -d 2 /var:可以进一步看到/var/log下面,是/var/log/nginx大还是/var/log/syslog大。
-a(all):连文件也不放过默认情况下,du只显示目录的大小。但有时候,一个目录本身不大,里面却藏着一个巨大的单个文件(比如一个数GB的数据库备份文件.sql)。这时候-a选项就派上用场了,它会同时列出目录和文件的大小。不过要注意,这会让输出变得非常冗长,通常需要结合sort和head来过滤。例如,找出当前目录下最大的10个文件:du -ah . | sort -rh | head -n 10。
-c(total):最后来一个总计这个选项会在输出的最后一行,加上所有列出项的总大小。在和-s一起使用时特别有用。比如du -sch /home/*,它会列出/home下每个用户目录的大小,并在最后给出一个总计。这在做空间统计报告时非常清晰。
--exclude与--exclude-from:排除干扰项磁盘分析中经常遇到一些“干扰项”,比如虚拟文件系统/proc、/sys,或者特定的缓存目录、版本控制目录(如.git)。直接扫描整个根目录可能会陷入这些特殊目录(它们的大小显示可能异常或没有意义)。--exclude=PATTERN可以排除匹配模式的文件或目录。
- 实战例子:你想分析根目录,但排除
/proc、/sys、/mnt这些挂载点,命令可以写成:du -sh --exclude=/proc --exclude=/sys --exclude=/mnt /*。 - 更复杂的情况,你可以把要排除的模式写在一个文件里,然后用
--exclude-from=FILE来指定。
2.2 理解“大小”背后的计算逻辑:磁盘占用 vs 实际大小
这里有一个非常重要的细节,也是新手容易困惑的地方:du命令默认显示的是“磁盘占用空间”,而不是文件的“逻辑大小”。
- 逻辑大小 (Apparent Size):就是文件内容实际有多少字节。可以用
ls -l看到的那个大小。 - 磁盘占用空间 (Disk Usage):是文件在磁盘上实际占用了多少块(block)。因为文件系统有“块大小”(block size,通常是4KB)的概念,即使一个文件只有1字节,它也会独占一个4KB的块。
举个例子:你创建了一个1字节的文本文件。
ls -lh file.txt可能显示1B(逻辑大小)。du -h file.txt很可能显示4.0K(磁盘占用,因为占了一个4KB的块)。
du命令之所以叫“disk usage”,就是因为它汇报的是对磁盘空间的真实消耗,这对于空间管理来说才是最有意义的。如果你想看逻辑大小,du提供了--apparent-size选项,但日常运维中极少使用。
2.3 一个综合性的实战案例
假设你是一台Web服务器的管理员,收到告警/分区空间不足。你的排查思路可以是这样:
快速定位问题一级目录:
cd / sudo du -h -d 1 | sort -hr这条命令会列出根目录下所有一级子目录的大小,并从大到小排序。你可能会立刻发现
/var或者/home异常巨大。深入问题目录,进行二级诊断: 假设发现是
/var很大。cd /var sudo du -h -d 1 | sort -hr现在你看到可能是
/var/log或/var/lib很大。精准定位大文件或历史文件: 进入
/var/log,你想找出最大的文件,并看看有没有陈年老日志。cd /var/log # 找出最大的10个文件 sudo find . -type f -exec du -h {} + | sort -rh | head -n 10 # 找出超过100M的文件 sudo find . -type f -size +100M -exec du -h {} \; # 找出30天前的日志文件 sudo find . -type f -name "*.log" -mtime +30 -exec ls -lh {} \;这里引入了
find命令进行更复杂的过滤。你可能发现是某个access.log文件滚了几十GB,或者一些旧的journal日志没清理。
通过这个由浅入深的流程,你就能像侦探一样,层层剥茧,最终找到吞噬磁盘空间的“元凶”。而这一切的起点,就是熟练运用du命令的各种选项。
3. sort命令精讲:让杂乱数据瞬间有序
如果说du是收集数据的侦察兵,那么sort就是整理情报的分析师。它能够对文本行进行排序,默认按照字典序(lexicographical order)升序排列。但面对du输出的带有K、M、G单位的数据,默认排序会完全失效(因为10M在字典序上会比2G“大”,这显然不对)。因此,我们必须掌握sort的高级用法,才能正确理解空间分布。
3.1 应对“人类可读大小”排序的关键选项
-h(human-numeric-sort):核心中的核心这是让sort能正确理解du -h输出的2K,150M,1.2G这类带单位大小的关键选项。它会智能地解析这些字符串,将其转换为统一的数值进行比较,从而实现从大到小或从小到大的正确排序。没有-h选项,对du -h的输出进行排序是毫无意义的。
-r(reverse):反转排序结果默认排序是升序(从小到大)。加上-r就变成降序(从大到小)。在分析磁盘空间时,我们几乎总是想先看最大的那些,所以-r和-h经常联用:sort -hr。
-n(numeric-sort) 与-h的区别这是一个常见的混淆点。-n是按数字排序,但它要求字符串以数字开头。对于du不带-h的输出(纯数字,单位是KiB),用-n排序是正确的。例如,du -s输出1024和20480,用sort -n能正确排序。但一旦用了-h,输出变成1.0M和20M,-n就无法解析了,必须用-h。
简单对比表:
| 场景 | du命令输出示例 | 正确的sort选项 | 错误选项及后果 |
|---|---|---|---|
| 查看人类可读大小 | 4.0K,1.2M,500G | sort -hr(降序) | 用sort -n会乱序,因为500G开头是字母 |
| 查看原始KiB大小 | 4096,12582912 | sort -nr(降序) | 用sort -hr可能出错,因为12582912没有单位 |
3.2 进阶技巧:多列排序与去重
sort的功能远不止于此,结合其他场景也能发挥巨大作用。
-k(key):指定排序的列du -h的输出默认是“大小 路径名”。当你使用-a选项列出文件时,或者用find结合du时,路径名可能包含空格。sort默认以空白字符(空格、制表符)作为分隔符,按第一列排序。但如果你想按第二列(文件名)的字母顺序排呢?或者,当输出格式变化时,你需要指定按哪一列排序。
du -ah . | sort -hr:这是按第一列(大小)降序排,最常用。du -ah . | sort -k2:这是按第二列(路径名)的字典序升序排。
-u(unique):去重有时du的扫描可能会因为软链接等原因导致路径重复计算(虽然du默认会避免跟踪软链接)。或者你从多个来源合并了列表,可以用sort -u来去除重复行。注意,它是在排序的基础上去重。
一个综合例子:分析日志目录,按日期和大小排序假设你的应用日志按日期分割:app-2023-10-01.log,app-2023-10-02.log… 你想找出最大的几个日志文件,同时看看它们的日期。
find /var/log/myapp -name "app-*.log" -exec du -h {} \; | sort -hr | head -5这条命令会找出/var/log/myapp下所有app-开头的日志文件,计算大小,然后按从大到小排序,最后只显示前5个。结果一目了然,你知道是哪几天的日志最“胖”。
4. 黄金组合实战:从基础排查到高级空间分析
掌握了du和sort的各自本领后,我们就可以将它们组合起来,解决各种复杂的磁盘空间问题。下面我分享几个从简单到复杂,从通用到特定场景的实战命令组合,这些都是我多年运维工作中积累下来的“干货”。
4.1 基础必备:快速定位目录空间占用TOP榜
这是最经典、使用频率最高的组合。无论你在哪个目录,当你感觉空间紧张时,首先应该运行它。
命令:
sudo du -h -d 1 | sort -hr分解与解读:
sudo:因为很多系统目录(如/var,/usr)需要root权限才能读取所有文件信息。在用户目录下可以不加。du -h -d 1:以人类可读格式,统计当前目录下所有一级子目录和文件的磁盘占用。注意,-d 1在这里是关键,它让我们聚焦于直接子项,不会陷入过深的递归。|(管道):将du命令的输出,作为sort命令的输入。sort -hr:-h人类数字排序,-r反转(降序)。这样最大的项就排在最前面。
输出示例:
4.6G ./project_data 2.1G ./logs 780M ./cache 120M ./config 4.0K ./README.md一眼就能看出,project_data目录是空间消耗的主力。接下来你就可以cd project_data,然后再次运行du -h -d 1 | sort -hr,进行下一层钻取。
4.2 进阶搜索:揪出隐藏的巨型文件
有时候,空间被一个巨大的文件占用了,但这个文件可能藏在很深的目录层级里。用上面的方法一层层cd和du虽然有效,但不够直接。我们可以用find命令来全域搜索大文件,再结合du和sort来展示。
命令1:找出当前目录及子目录中最大的10个文件
find . -type f -exec du -h {} + 2>/dev/null | sort -rh | head -n 10分解与解读:
find . -type f:在当前目录.下,查找类型为文件(f)的所有项。-exec du -h {} +:对找到的每一个文件,执行du -h命令来获取其大小。{}是占位符,代表找到的文件名。+表示将多个文件一次性传递给du命令,效率比\;高。2>/dev/null:将错误信息(如权限不足)重定向到“黑洞”,让输出更干净。注意:这可能会隐藏一些重要错误,在需要完整信息时可以去掉。sort -rh:按大小降序排。head -n 10:只显示前10行。
命令2:找出大于特定尺寸的文件(例如大于100MB)
find / -type f -size +100M -exec du -h {} \; 2>/dev/null | sort -rh-size +100M:查找大小超过100MB的文件。单位可以是k(KB),M(MB),G(GB)。- 这个命令从根目录
/开始搜索,范围广,可能需要较长时间和root权限。建议在知道问题大概范围时,将/替换为更具体的路径,如/home或/var。
4.3 特定场景清理:日志、缓存与临时文件
许多磁盘空间问题都源于日志、缓存和临时文件的无限制增长。下面是一些针对性的清理前分析命令。
分析日志目录(如/var/log):
# 查看各日志子目录大小 sudo du -h -d 1 /var/log | sort -hr # 查看最大的10个日志文件(包括归档的.gz文件) sudo find /var/log -type f \( -name "*.log" -o -name "*.gz" \) -exec du -h {} + | sort -rh | head -10分析用户缓存(如~/.cache):
# 查看缓存目录下哪些软件缓存最大 du -h -d 1 ~/.cache | sort -hr # 常见的可清理目标:浏览器缓存、软件包管理器缓存(apt/yum/dnf)、IDE缓存分析Docker磁盘占用(如果使用了Docker):Docker是个典型的空间消耗大户,镜像、容器、卷都会占用大量空间。
# 查看Docker总体磁盘使用 docker system df -v # 结合du查看Docker数据目录(默认/var/lib/docker)的详细分布 sudo du -h -d 2 /var/lib/docker | sort -hr | head -204.4 生成可视化报告:给领导或自己一个清晰视图
当你需要定期巡检服务器磁盘空间,或者向团队报告存储使用情况时,一个格式清晰的报告很有用。我们可以将命令输出重定向到文件,并稍作格式化。
生成一个简单的空间使用报告:
{ echo "=== 服务器 $(hostname) 磁盘空间分析报告 ===" echo "生成时间: $(date)" echo "" echo "【根目录一级子目录大小排行】" sudo du -h -d 1 / | sort -hr echo "" echo "【/var目录详细分析】" sudo du -h -d 2 /var | sort -hr | head -15 echo "" echo "【大于1GB的文件列表】" sudo find / -type f -size +1G -exec du -h {} \; 2>/dev/null | sort -rh } > ~/disk_usage_report_$(date +%Y%m%d).txt这个脚本会将分析结果保存到一个带有时间戳的文本文件中,内容结构清晰,包含了概况、重点目录分析和特大文件列表。
5. 避坑指南与性能优化心得
工具虽好,但使用不当也会踩坑,尤其是在生产环境执行时。下面是我总结的几个常见问题和优化技巧。
5.1 权限不足与“拒绝访问”
当你使用sudo du扫描系统目录时,基本不会遇到问题。但在扫描像/home这样包含多个用户目录的地方时,即使有sudo,du也可能因为无法进入某些用户的.private目录或遇到特殊权限文件而报错。命令会继续执行,但输出中会夹杂Permission denied的错误信息,干扰阅读。
解决方案:
- 忽略错误(最常用):将标准错误重定向到
/dev/null。
这会让输出非常干净。但代价是你完全看不到任何错误,包括那些可能提示你存在异常权限配置的错误。sudo du -h /home 2>/dev/null | sort -hr - 分离错误(推荐):将错误信息输出到另一个文件,既保持主输出干净,又保留了错误日志供排查。
sudo du -h /home 2> /tmp/du_errors.log | sort -hr # 之后可以查看错误日志 cat /tmp/du_errors.log | head -20
5.2 扫描速度慢与系统负载
在拥有海量小文件(例如代码仓库、邮件存储、日志碎片)的目录上运行du,可能会非常慢,并且导致磁盘I/O升高,在繁忙的生产服务器上需要谨慎。
优化策略:
- 明确目标,限定范围:不要动不动就
sudo du -sh /。先通过df -h命令看哪个挂载点空间紧张,然后只针对那个挂载点下的主要目录进行扫描。 - 使用
--time选项预估:du命令本身没有进度条。但你可以先在一个子集上运行,估算总时间。或者,使用--time选项让它显示每个目录的耗时,帮你识别出扫描特别慢的“瓶颈”目录。 - 考虑文件系统特性:对于某些现代文件系统(如ZFS, Btrfs),它们可能提供更快的空间使用查询命令(如
zfs list)。du是通用工具,但在特定文件系统上未必是最优解。 - 避免在高峰时段操作:如果非紧急,将磁盘分析任务放在业务低峰期进行。
5.3 符号链接(软链接)的陷阱
du命令默认情况下不会跟踪符号链接(symlink)。它会统计符号链接文件本身的大小(通常很小),而不是它指向的目标文件或目录的大小。这可能导致你误判。
例子:
$ ln -s /var/log/mysql ./mysql_log_link $ du -sh mysql_log_link 4.0K mysql_log_link # 这只是链接本身的大小 $ du -shL mysql_log_link 180M mysql_log_link # 这是跟踪链接后,实际目标的大小关键选项:-L(dereference)使用-L选项,du会跟踪符号链接,并统计其指向的真实目标的大小。在分析可能包含大量软链接的目录时(例如某些软件安装目录),要注意是否需要加上-L来获得真实的空间消耗。
5.4 脚本化与自动化监控
对于需要定期检查的服务器,我们可以将这套组合拳写成脚本,加入定时任务(cron)。
一个简单的每日空间检查脚本示例 (check_disk_usage.sh):
#!/bin/bash # 简单磁盘空间检查脚本 LOG_FILE="/var/log/disk_check.log" THRESHOLD=80 # 使用率告警阈值,单位% echo "===== 磁盘检查报告 $(date) =====" >> $LOG_FILE echo "" >> $LOG_FILE # 1. 使用df查看整体使用情况 echo "【整体磁盘使用率】" >> $LOG_FILE df -h | grep -E '^/dev/' | awk '{print $1, $5, $6}' | while read device usage mount; do usage_percent=${usage%\%} if [ $usage_percent -gt $THRESHOLD ]; then echo "警告: $device 挂载于 $mount 使用率 ${usage},超过阈值 ${THRESHOLD}%" >> $LOG_FILE # 2. 如果根分区超过阈值,则分析大目录 if [ "$mount" == "/" ]; then echo "开始分析根分区大目录..." >> $LOG_FILE sudo du -h -d 2 / | sort -rh | head -10 >> $LOG_FILE 2>/dev/null fi else echo "正常: $device 挂载于 $mount 使用率 ${usage}" >> $LOG_FILE fi done echo "" >> $LOG_FILE然后通过crontab -e添加一行,每天凌晨3点运行:
0 3 * * * /root/scripts/check_disk_usage.sh这样,你每天只需要查看/var/log/disk_check.log文件,就能对服务器的磁盘健康状况有一个清晰的了解,并在问题发生前得到预警。
du和sort的组合,其精髓在于将复杂的空间分析问题,分解为“测量”和“排序”两个简单的步骤。它不依赖于任何图形界面,在所有的Linux发行版和远程SSH会话中都能稳定工作。从一次紧急的磁盘空间告警处理,到日常的存储资源规划,这套命令都是最可靠的工具。我个人的体会是,花时间熟练掌握它们,比盲目安装各种图形化监控工具更有效,因为它给了你最深层的控制力和洞察力。下次当你再遇到“磁盘空间不足”的提示时,希望你能自信地打开终端,敲下du -h -d 1 | sort -hr,开始你的排查之旅。