我最早遇到这个需求,是在一次线上磁盘告警的时候。应用日志目录把根分区吃到了90%以上,我第一反应就是先看看到底是哪个子目录在“疯狂增长”,但当时我下意识敲出来的命令是 du -sh *——结果输出了一堆单文件的大小和一个小到离谱的目录汇总,完全不是我想要的。后来才把 du --max-depth=1 这个组合彻底用熟。这篇文章就专门讲清楚:怎么用一个命令,只列出当前目录下第一层子目录各自占了多少空间,以及这个命令背后那些不看文档就很容易踩的坑。
1. 为什么明明有 du -sh,却还是看不到每个子目录的大小
先解决一个最基础的疑惑:很多人不是不知道 du,而是用错了参数。du 的核心逻辑是“递归计算磁盘占用”,所以它的默认行为是一层一层往下钻,把每个子目录、孙目录全部列出来。真正让你“只看一层”的,不是 -s,而是 --max-depth。
1.1 du -sh 到底在算什么
拆开看:
-s是--summarize的缩写,意思是“只显示总计,不显示细节”。-h是--human-readable,自动把字节数换算成 K、M、G。
所以 du -sh /data 的结果是 /data 这一整个目录树的磁盘占用总和,它不会告诉你 /data/log、/data/lib、/data/tmp 分别多大。-s 存在的意义是“我只要一个总数”,而不是“我要看明细”。
那为什么有人会觉得 du -sh * 能看子目录大小?因为 * 在这里的语义是把当前目录下的每个条目作为独立参数传给了 du。系统会逐个计算 du -sh /data/log、du -sh /data/lib……从结果上看确实列出了每个子目录的大小,但这里隐藏着至少三个问题:
- 如果没有关闭 glob 展开或者目录条目特别多,
*展开后的参数列表可能超过ARG_MAX,直接报 “Argument list too long”。 *只能匹配非隐藏条目,.开头的目录会被自动忽略。- 如果当前目录下还有普通文件,
du -sh *会把它们也混在一起输出,你拿到的是“目录 + 文件”的混合列表,字段内容还说不上混乱,但确实不够纯粹。
1.2 反直觉的事实:裸 du 会递归到每一层
很多人第一次用 du 的时候,会顺手敲一个 du /data,结果发现输出几十行甚至几千行,每个子目录都带着一个数字。这不是命令坏了,而是 du 的默认深度就是“无穷大”,它会递归完整个目录树。
而且 du 的输出顺序也不是按大小排的,基本是文件系统目录项的遍历顺序。目录项的顺序由文件系统创建时间、删除后的重用情况决定,完全没有规律可循。所以如果你直接看裸 du 的输出,想从中找到最大的子目录,基本等于大海捞针。
1.3 ls -l 里的目录大小为什么是假的
还有一个容易混淆的点,就是 ls -ld /data 显示的大小。那个数字通常是 4096 或 4096 的整数倍,很多人误以为这就是目录的真实大小。
实际上,ls 显示的目录大小,只是目录文件本身(目录项数据结构占用的空间),也就是存放“子目录名、文件名、inode 号”这些元信息的空间。真正的内容——文件数据块、子目录里的所有递归内容——都不在目录文件里。所以 4096 这个数字基本是固定不变的,跟目录里装了多少数据没有关系。这也解释了一个挺常见的热搜问题:ls -l 第一行输出的 total 到底是什么意思。它表示当前目录下所有条目占用的磁盘块总数,单位通常是 1024 字节块,但这里的计算结果也不等于 du -sh 的汇总值,两者计算逻辑不同,别混着用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. du --max-depth=1:只输出第一层子目录的完整用法
这才是正题。--max-depth 是 GNU coreutils 里 du 提供的一个递归深度控制参数,语义非常直观:最多递归到第几层。
2.1 深度参数与控制逻辑
完整写法是:
bash复制du -h --max-depth=1 /data
也可以简写成 -d:
bash复制du -h -d 1 /data
两条命令完全等价。--max-depth=1 的含义是:计算 /data 下的第一层子目录的大小,并输出 /data 本身的总大小。
深度值的规律:
| 参数 | 输出内容 |
|---|---|
-d 0 |
等价于 -s,只输出当前目录树的总大小 |
-d 1 |
输出当前目录本身 + 第一层子目录的大小 |
-d 2 |
输出当前目录本身 + 第一层子目录 + 第二层子目录的大小 |
如果你只想要子目录的明细,不想要当前目录那一行汇总,可以配合 tail 或 grep 过滤。使用 GNU du 时,最后一行通常是当前目录的总大小(实际顺序取决于 shell 通配符和参数顺序),一般习惯是:
bash复制du -h --max-depth=1
