刚接手一台告警的服务器,磁盘使用率飙到97%,SSH上去之后我没有急着du -sh到处刨,而是先敲了一条tree -L 2 --du /,把根目录下面的大结构一次性铺开。很多人觉得 tree 就是个"画目录树"的小玩具,但在磁盘管理这个场景下,它其实是帮我们建立空间认知的第一块拼图——目录长什么样、东西堆在哪里、哪个分支最臃肿,一眼就能看明白。这篇实操篇就围绕"磁盘管理 + tree 命令"来展开,完整过一遍安装、常用参数、分析套路、脚本化输出,以及我踩过的几个坑,希望能帮你把这把刀真正磨快。
1. 磁盘管理的日常里,tree命令解决的是"看清问题"这一步
1.1 tree到底解决了哪种痛点
先讲个我在真实环境里的感受:磁盘告警出现后,绝大多数人第一反应是df -h看哪个分区满了,然后du -sh *在某个目录下打一圈。可是当目录层级变深、文件数量变多的时候,du的输出是一长串没有层级的平铺列表,看着看着就乱了——你根本想不起来/var/lib/docker/containers和/var/log/journal之间到底谁是谁的子目录。
tree 命令在这里的价值不是替代du,而是补上结构维度。它把目录的父子关系用树状图画出来,相当于给磁盘空间分析加了一张地图。有了地图再跑du,你就知道这一步纵深进去的是哪个分支,不会在几十个相似目录名里来回折腾。
从运维的角度说,tree 最让我依赖的场景有三类:
- 排查大目录时先看"轮廓",比如
/var下面到底挂着哪些子目录,哪些是日志、哪些是缓存、哪些是容器数据,先分清类别再去定位体量。 - 交付项目或业务系统时做目录结构快照,把关键路径的层级关系输出成文档,给同事或客户看,比一张截图的表达能力强得多。
- 日常巡检脚本里定时抓取目录树变化,配合文件数量统计,能快速发现"多出来的一层目录"或"不断增长的缓存目录",这类问题靠单纯的磁盘用量曲线往往看不出来。
一句话总结:du告诉你怎么分地,tree告诉你地是怎么划的。两者配合才能把磁盘管理的活干利索。
1.2 和ls -R、find、du的定位差异
很多人会问:ls -R不也能递归列目录吗?find 也能列文件,为什么还要单独学 tree?
这个问题我实际反复比对过。ls -R的输出问题在于没有层级关系,它只是把所有子目录的内容按目录分块罗列出来,目录深度超过三四层以后,你根本看不出谁嵌套在谁里面。而且它默认不区分目录和文件的视觉层级,普通人看着就是一团糊。
find更不是干这个的。find 的强项是按条件搜索,比如找大于100M的文件、找三天前修改的文件,它的输出天然是扁平的路径列表。find 擅长"找出目标",tree 擅长"呈现全貌",这是两种思维方式。
至于du,侧重的是空间占用数值。du -h --max-depth=1可以告诉你每个一级子目录占多少G,但它不会告诉你这个目录下面内部结构怎么分布——而这恰恰是分析"空间去哪了"时最花时间的一步。
所以就我们这篇的主题——磁盘管理——而言,我认为最顺手的组合拳是:
df -h # 看分区整体水位 tree -d -L 2 /var # 看某个大目录的骨架结构 du -h --max-depth=1 /var # 看每个子目录的空间占用树在前面铺结构,du 在后面量大小,两步下来问题基本能锁定到具体路径。这个套路我用了很多年,效率远高于单纯在du的输出里瞎翻。
2. 先把手上的环境装好:不同发行版的tree安装与第一个命令
2.1 各发行版的安装差异,别在这些地方浪费时间
tree 是一个非常轻量的软件包,大多数发行版默认不装,但它也不是什么冷门东西,装起来一分钟以内搞定。问题在于不同发行版的包管理器命令不一样,我见过不少新手在 CentOS 上敲apt install tree报错后就开始怀疑人生,其实只是没选对命令。
常见的几个系列:
| 发行版 | 安装命令 | 备注 |
|---|---|---|
| Ubuntu / Debian | sudo apt install tree | 通常需要在apt update之后执行 |
| CentOS / RHEL / Rocky / AlmaLinux | sudo yum install tree | 老版本用 yum,8 以上也可以直接dnf install tree |
| Fedora | sudo dnf install tree | dnf 是默认包管理器 |
| openSUSE | sudo zypper install tree | SUSE 系列的 zypper |
| Arch / Manjaro | sudo pacman -S tree | 滚动更新,装完即用 |
| macOS | brew install tree | 如果你在 Mac 上做开发,Homebrew 装最快 |
安装完成后验证一下版本:
tree --version如果能看到类似tree v2.0.2 (c) 1996 - 2022 by Steve Baker, et al.的输出,说明装好了。旧版本和新版本的参数有细微差别,比如--du和--timefmt在较老版本里行为不完全一致,后面遇到参数不生效的问题时先看一眼版本。
一个小经验:在容器环境里,如果基础镜像太精简连apt都没有,可以先cat /etc/os-release确认是哪个发行版,再决定用 apt 还是 yum。我在排障时候见过不少人在 Alpine 里敲apt,然后折腾半天发现应该用apk add tree。先看系统再动手,能省下一大堆时间。
2.2 基本语法和第一张目录树
tree 的基本语法结构非常简单:
tree [选项] [目录]不带任何参数直接运行tree,它会从当前目录开始递归展示所有的子目录和文件。我用一个典型项目目录举例,实际运行效果类似这样:
$ tree . ├── README.md ├── docs │ ├── api.md │ └── guide.md ├── scripts │ ├── deploy.sh │ └── backup.sh └── src ├── main.go └── utils └── logger.go 3 directories, 7 files注意最后一行,tree 会自动统计"目录数"和"文件数"。这个小细节在磁盘管理里很有用,因为它能直观告诉你一个目录下到底挂了多少东西,而不是只看到字节数。比如排查 inode 耗尽的时候,tree -a统计出几十万个文件,定位方向就和"空间被占满"完全不同了。
指定目录也很直接:
tree /var/log tree -L 1 /etc-L 1表示只显示一层,-L 2就是显示两层,这个参数是控制输出规模的关键。生产环境千万不要直接对/跑不带深度的 tree,那个输出能把终端刷到怀疑人生,甚至把系统拖慢。我第一次学 tree 的时候在虚拟机里对/跑了一次,结果终端卡了好一会儿,心有余悸。
3. 实操核心:把tree变成磁盘占用分析的放大镜
3.1 用tree -h和--du直接看到文件真实大小
如果你以为 tree 只是"画树",那接下来这个用法会改变你的看法。tree 自带显示文件大小和统计目录总大小的能力,这是它在磁盘管理中真正拉开身位的地方。
先看最常用的组合:
tree -h --du /var/log-h是 human-readable,把文件大小显示成 K、M、G 这样的单位,而不是字节。这个和ls -lh的-h是一个思路。--du是 disk usage 的缩写,作用是递归计算并显示每个目录的总大小。
运行之后,你会发现每个目录名后面带了一个方括号,里面是这个目录下所有内容加起来的体积。比如:
/var/log ├── [ 12K] alternatives.log ├── [ 48K] apt │ ├── [ 0] history.log │ └── [ 48K] term.log ├── [724K] journal │ └── [724K] 9d6c1f2c4e0c4f6c8f8c2a7f2d1e3b4f └── [ 16K] syslog 3 directories, 5 files注意细节:目录后面方括号里的是该目录递归总计大小,文件后面的是文件本身大小。这在定位"哪个分支吃了最多空间"时特别好用——你不用再对着一堆文件名猜,目录后面的体积直接帮你锁定了嫌疑对象。
再配合深度限制使用,效果更佳:
tree -h --du -L 2 /var它会先给你看/var下面一级子目录各自的总大小,再往下展开一层。这基本等同于du -h --max-depth=2的可视化版本,但比 du 多了一个优势:目录层级关系直接可见。你一眼就能看出/var/lib/docker下面那堆空格是被哪个镜像或者某个 overlay 目录吃掉的,不需要反复 cd 进目录再执行 du。
3.2 按场景组合:日志目录、家目录、根分区的排查套路
不同的磁盘排查场景,tree 的用户法略有差异。我按平时最常遇到的几类问题整理一下。
场景一:日志分区增长异常
tree -h --du -L 2 /var/log重点关注journal和apt这类累积目录。journal如果体积异常大,说明journald的日志保留策略可能需要调整;apt下面的缓存目录变大了,可以用对应的 clean 命令清理。tree 在这里的最大价值是看层级聚合体积,比ls -lh一个个看文件高效得多。
场景二:家目录占用异常
tree -h --du -L 1 ~很多用户的家目录里藏着缓存的软件包、npm 缓存、docker 镜像、conda 环境之类的巨无霸。-L 1先看一级子目录的体积分布,锁定目标后再tree -h --du -L 3 ~/.cache深入。通常这样走两三步就能找到问题所在。
场景三:根分区直接满了,需要快速判断删除方向
tree -h --du -L 1 /注意,对根目录执行时最好加-L 1,否则输出量非常恐怖。一级目录的体量出来后,一般问题集中在/var、/home、/tmp这几个地方,再逐层深入。整个过程其实就是在"体积聚合视图"里从上往下钻,每一步都有数据支撑,不会像无头苍蝇一样乱翻。
有一点必须提醒:--du统计的结果和du命令并不完全一样。tree 的--du默认统计的是文件大小总和,不是磁盘块占用。对于一个全是小文件的目录,文件大小总和可能明显小于实际占用的磁盘块数;反过来,稀疏文件则可能让总和偏大。在精确定量时还是要以du -sh为准,tree 负责的是"快速筛选和定位",两者定位不同,配合使用而不是互相替代。
4. 进阶玩法:把tree的输出变成巡检和文档的素材
4.1 只显示目录或只过滤匹配文件:tree的条件筛选
日常分析和脚本化输出时,并不是永远都需要把所有文件都列出来。tree 提供了相当实用的筛选参数,盘一盘我用得最多的:
tree -d # 只看目录,不看文件 tree -P "*.log" # 只显示匹配 *.log 的文件 tree -I "node_modules" # 排除 node_modules 目录 tree -P "*.conf" -I "*.bak" # 匹配 conf 文件,同时排除 bak 文件-d在磁盘管理里非常实用,特别是你想快速了解某个目录下面"到底建了多少层目录结构"时。比如:
tree -d -L 3 /var/lib/docker这条命令能让你快速看清 docker 的目录骨架:containers、image、overlay2、volumes这些关键目录各在哪里,几个层级内就能摸清。对于镜像清理、卷迁移这类操作来说,这个结构感知相当重要。
-P和-I用的场景是找某类文件。我举一个真实案例:排查某个目录下分散在各处的.core文件(程序崩溃产生的核心转储),一条命令全部拎出来:
tree -P "*.core" -h /data/app它会把所有匹配文件在树中位置展示出来,同时又保留了它们所在的目录上下文。相比之下,find /data/app -name "*.core"只能给你一堆平铺路径,你还得自行脑补目录结构。这在写清理脚本时很省事,因为你马上知道这些文件分布在哪些目录,清理策略是统一删还是按目录保留,一眼就能判断。
4.2 让tree输出能被机器读取:JSON、XML、HTML吃透
如果你以为 tree 只能输出给人看的文本,那格局就小了。tree 支持直接输出 JSON、XML 和 HTML 格式,这是它进阶为"自动化巡检日报"的重要能力。
生成 JSON 格式的目录树:
tree -J --du /var/log > /tmp/log_tree.json-J生成 JSON,配合--du可以把体积信息也带进去。我在写磁盘巡检脚本时就经常用这个功能:定时生成关键目录的 JSON 结构,然后用 jq 解析出体积超过阈值的目录,自动发告警。用 tree 输出 JSON 的好处是,目录层级关系是天然嵌套的,jq 解析起来非常顺手,不用自己写一堆递归逻辑。
生成 HTML 格式快照:
tree -H /var/log -o /tmp/log_tree.html-H生成一个带链接的 HTML 页面,里面的目录和文件会带上 file:// 形式的超链接。这个功能用来做项目结构展示页或者内部文档非常方便,打开浏览器就能点着看。不过要注意,-H生成的是相对路径,放到别的机器上最好配合-T指定标题,避免页面光秃秃的。命令可以这样:
tree -H /var/log -o /tmp/log_tree.html -T "log目录结构"推送 HTML 到文档系统时,我建议再加--noreport,把尾部那个 "N directories, N files" 统计去掉,视觉上更干净。
4.3 和大文件查找命令的组合:tree + du + find 的流水线
最后说一下我自己在故障排查时常用的完整流水线。磁盘告警时,我会用 tree 做结构定位,用 du 做体积确认,用 find 做精确清理,三者串起来:
tree -d -L 2 /var # 第一步:看结构 du -h --max-depth=2 /var | sort -hr | head # 第二步:看数值,排出前几名 find /var -type f -size +1G -exec ls -lh {} \; # 第三步:精确找出巨型文件这套组合的优势在于每一阶段都有上一阶段的输出做指引。tree 帮你确定该往哪个目录钻,du 帮你确认这个目录到底多大,find 帮你找出具体文件,最后清理的时候有据可依、不会误删。如果你一上来就 find 大文件,可能找到的是一堆不在"真正问题目录"里的无关大文件,方向容易跑偏。
5. 参数速查表和实战中的坑
5.1 高频参数一表看懂
下面这张表整理了我脑海中"最常用"的 tree 参数,按使用频率大致排列:
| 参数 | 作用 | 示例 |
|---|---|---|
-L n | 限制显示深度为 n 层 | tree -L 2 /etc |
-d | 只显示目录 | tree -d /var |
-h | 以可读单位显示大小 | tree -h --du /var |
--du | 递归统计目录总大小 | tree --du -L 1 /home |
-P pattern | 只显示匹配的文件/目录 | tree -P "*.conf" |
-I pattern | 排除匹配的文件/目录 | tree -I "node_modules" |
-a | 显示隐藏文件(默认不显示) | tree -a ~ |
-f | 打印完整路径前缀 | tree -f /var/log |
-o filename | 输出到文件 | tree -o /tmp/tree.txt |
-J | JSON 格式输出 | tree -J /var/log |
-H | HTML 格式输出 | tree -H /var/log -o out.html |
--noreport | 不显示末尾统计行 | tree --noreport /var |
--timefmt | 配合-D自定义时间格式 | tree -D --timefmt "%Y-%m-%d" |
-s | 显示字节大小 | tree -s |
--prune | 跳过空目录,让树更紧凑 | tree --prune /var/log |
这些参数不用背,用的时候查表即可。但要记住一个重要的组合习惯:-h和--du经常一起用,-d和-L n经常一起用,这两个组合几乎能满足 70% 的磁盘管理场景。
5.2 中文乱码、链路死循环、权限噪音三个常见坑
参数背熟了,接下来是实战中最容易翻车的三个坑。
中文文件名乱码问题。某些系统环境下 tree 输出的中文文件名变成一串数字编码,比如\344\270\255\346\226\207。这通常不是文件本身的问题,而是终端和 tree 之间的编码显示不匹配。解决办法不复杂,如果你确认系统是 UTF-8 环境,可以在命令前设置语言环境变量:
export LANG=en_US.UTF-8 export LC_ALL=en_US.UTF-8或者直接用LC_ALL=C.UTF-8 tree。如果 locale 里没有 UTF-8,先locale -a看一下有哪些可用,再export LANG=zh_CN.UTF-8。另外 Windows 上的 Git Bash 或 MobaXterm 连 Linux 时也容易出现类似问题,优先检查终端会话的字符集设置。
符号链接导致树无限延伸。如果你的目录里存在指向父目录的符号链接,tree 默认不会跟进去,所以一般不会死循环。但当使用-l(跟随符号链接)参数时,tree 会顺着链接往下走,遇到环状链接会造成非常深的递归输出。我曾经在一个备份目录里遇到过软链接指回上级目录的情况,配合-l跑出来的树长得离谱,差点让人以为是程序卡死。处理办法是:非必要不加-l,必须在链路场景下使用时,一定用-L n限制深度,比如:
tree -l -L 5 /data/backup权限报错噪音过重。对/或某些系统目录执行 tree,会输出大量的Permission denied提示,把有用的目录树冲刷得根本没法看。此时可以加-x参数让 tree 停留在当前文件系统(不跨越挂载点),减少无关路径的输出;更好的办法是加--prune跳过空目录、加--noreport去掉统计行,配合2>/dev/null丢掉权限报错:
tree --prune -x -L 3 / 2>/dev/null这样输出立刻干净许多。
5.3 关于性能:大目录别硬刚
tree 在处理超大目录时性能并不算出色,尤其加了--du之后,它内部要递归统计每个目录的体积,对于几十万文件的大目录会耗时较长。我的建议:
- 对
/var/lib/docker、/home这类大目录,先用-L 1或-L 2控制规模,不要贪心一次展开到底。 - 需要完整 JSON 或 HTML 快照时,放到凌晨巡检脚本里跑,输出到文件而不是直接打到终端。
- 如果在生产环境想要"比较完整"但又不卡顿,可以用
tree -a --dirsfirst --du -L 3这样的折中方案,既能看层次也能看体积,输出量又在可控范围内。
记住一个原则:tree 是快速认知工具,不是全量审计工具。全量审计交给 find、du、jq 这些更强的文本处理管线去干。
6. 一个真实故障案例:92%磁盘占用是怎么用tree十分钟定位的
最后分享一个让我印象深刻的真实案例。有一回线上某台跑日志采集的服务器发出磁盘告警,使用率到了92%,再不处理可能影响写入。我上去之后没有盲目删日志,而是按这套方法走的:
第一步,df -h确认/分区快满; 第二步,tree -h --du -L 1 / 2>/dev/null,一秒钟不到,发现/var占了将近 89G; 第三步,tree -h --du -L 2 /var 2>/dev/null,结果赫然显示/var/lib/docker里有个 overlay2 目录,接近 80G; 第四步,find /var/lib/docker -type f -size +500M -exec ls -lh {} \;,找到了几个废弃镜像的堆叠层文件。
整个过程从登录到定位,不到十分钟。后续确认是某个服务更新镜像后旧层没清理干净,执行镜像清理和日志轮转后,磁盘用量从 92% 降到了 41%。
这个案例想说明的其实是 tree 的定位思路:先从最高层看聚合体积,再逐层下钻,绝不在一开始就陷入文件名细节。有了树形结构的导航,磁盘空间分析就不再是撞运气式的查找,而是一个有路标、有层次的排查过程。
如果你正准备把 tree 纳入自己的日常工具箱,我的建议很直接:先从tree -L 2,tree -d -L 2和tree -h --du -L 1这三个命令入手,放到目录结构梳理和磁盘占用初筛里用起来。用顺手之后再考虑 JSON 输出和巡检脚本的集成,慢慢你会发现自己接下来对"某个目录结构长什么样"的直觉会越来越准,排查磁盘问题的速度也会上一个台阶。