
先说个场景你维护的 Windows 服务器磁盘又红了打开资源管理器一层层点这个目录几十 GB、那个目录十几个 GB点到最后手指都酸了。这时候你会怀念 Linux 下那个叫 du 的命令——一条du -sh *直接按目录给你排好谁大谁小一目了然。可惜 Windows 命令提示符里敲 du大概率只会提示“du 不是内部或外部命令”。那 Windows 上到底能不能用上 du或者说有没有跟 du 一样顺手、一样能打的办法这篇文章就把这事彻底讲清楚。我不会只给你一个“替代品”的名字就完事而是会把 Windows 上所有能实现 du 效果的路子都捋一遍微软官方命令行工具、图形化扫描器、Git Bash 和 WSL 白嫖原生 du、以及自己写 PowerShell 脚本。你完全可以按自己的使用场景直接“抄作业”。1. 先搞清楚Windows 为什么没有原生的 du1.1 du 到底解决了什么问题Linux 用户对 du 的感情很深原因不是它功能有多花哨而是它把“磁盘占用统计”这件事做得很纯粹。你给它一个目录它递归往下算把每个子目录、每个文件占用的磁盘块数汇总出来再以人类可读的大小展示。du -sh *这个组合拳能让你在三秒内知道当前目录下哪个文件夹最肥、哪个文件该清理。反观 Windows磁盘满了以后你只能打开资源管理器右键点属性看目录大小。这个操作有两个致命问题一是没法批量看一次只能看一层二是没有排序你根本不知道哪个子目录是罪魁祸首。哪怕是运维老手遇到一个塞了几万个小文件的目录用资源管理器一层层点属性也能点到怀疑人生。所以 Windows 用户真正缺的不是“统计磁盘占用”的能力缺的是一个称手的、能像 du 一样快速定位大目录的工具。du 这个名字虽然叫磁盘使用量但它在实际工作中的价值更像一个“容量体检器”帮你快速判断到底是谁把磁盘吃掉了。1.2 直接拿 Windows 自带命令硬上的三个痛点有人说 Windows 也不是完全没法统计dir /s也能算出目录总大小。这话没错但用起来相当难受我总结下来有三个痛点第一dir /s会把目录下所有文件列表刷屏式地打印出来几百上千个文件糊满屏幕后真正的汇总数字被淹没在末尾你得翻好几页才能找到。第二它只能给出一个目录的总大小不能按子目录、按文件类型去拆解无法回答“到底是哪个目录在膨胀”这个问题。第三它没有任何排序和过滤能力不能只列出最大的前十个目录也不能排除掉某个路径所有逻辑都得靠人眼从一长串输出里慢慢找。至于 PowerShell虽然Get-ChildItem -Recurse | Measure-Object -Sum能算出总大小但它在遍历大量小文件时性能很差一个几十 GB 的大目录常常要跑好几分钟。而且每次要写一长串管道命令说实话比 Linux 的 du 差远了。这些都是我在实际环境里踩过的坑所以后面会给出更靠谱的方案。2. 实现方案横评从命令行到图形工具的五种路子2.1 最正统的标准答案Sysinternals du.exe如果你想要一个和 Linux du 最接近的、纯命令行的工具首选是微软官方 Sysinternals 套件里的 du.exe。这个工具是微软自己出的免费不需要安装下载解压就能用也没有任何安全风险。实际用法非常简单du.exe -accepteula -nobanner -q D:\data这条命令会统计D:\data目录的总大小并输出字节数。加-v参数可以递归列出每个子目录的大小类似 Linux 的du -h --max-depth效果非常适合快速定位哪个子目录占了大量空间。加-s可以只给出汇总总和。它的输出格式是纯文本可以直接重定向到文件里做后续分析。Sysinternals du 最大的优势是它对 Windows 路径、长路径、权限模型处理得比 Linux 的 du 更顺手毕竟是原生 Windows 工具。它不会像资源管理器那样一遇到 Junction 链接就傻眼也不用担心路径分隔符的转换问题。缺点是它是命令行工具没有可视化界面对不熟悉命令行的朋友不太友好。另外它的统计速度属于中等偏上比 PowerShell 快很多但比不上后面说的图形工具那么夸张。它最适合的场景是服务器维护、脚本调用、需要把结果导出给其他程序处理的自动化场景。2.2 图形工具的正确打开方式WizTree 与 WinDirStat 怎么选如果你主要是在自己电脑上交互式地排查“C 盘怎么又满了”那我强烈建议直接用图形化工具省时省力。这类工具里我首推 WizTree它和 Linux 的 du 思路不同但效果更猛。WizTree 能快到一个什么程度呢扫描一块 1TB 的 NTFS 硬盘通常只需要几秒钟原因是它直接解析 NTFS 的主文件表MFT而不是像普通工具那样一个文件一个文件地遍历。MFT 相当于 NTFS 文件系统的“目录索引”记录了每个文件的文件名、大小、位置WizTree 直接读这个索引速度自然快得离谱。WinDirStat 则是更老牌的开源工具用色块来可视化每个文件占用的空间视觉效果很好但它是全盘遍历的速度比 WizTree 慢得多动辄需要几分钟甚至更久。还有一个常被提到的 TreeSize Free界面做得比较精致能按目录树层层展开统计免费版也够用。我个人选择逻辑是这样的日常要快速定位大文件用 WizTree想直观地看哪些文件类型占空间最多用 WinDirStat需要把目录树大小导出成报告再考虑 TreeSize Free。图形工具做交互排查很强但在自动化脚本里用不上这时候就得回到命令行工具。其实图形工具最大的价值在于“第一轮快筛”。我遇到磁盘告警第一件事就是开 WizTree 对整个盘扫一遍几秒钟就能锁定最大的那个目录然后再用命令行工具对这个目标目录做精确统计和清理效率比纯命令行高太多了。2.3 复用 Linux duGit Bash 与 WSL 的白嫖方案如果你电脑上已经装了 Git for Windows或者开了 WSLWindows Subsystem for Linux那你其实根本不需要额外装别的统计工具直接用 Linux 的原生 du 就行。Git for Windows 自带了一套 MSYS2 环境里面包含了 coreutils所以C:\Program Files\Git\usr\bin\du.exe是真实存在的。打开 Git Bash 后Windows 的盘符会被映射成 Linux 风格的路径C:\变成/c/D:\变成/d/。于是你可以直接这样用cd /d/data du -sh *这一下就和 Linux 上的体验完全一样了还能用du -h --max-depth1按目录深度展开用du -h --max-depth1 | sort -hr按大小排序。Git Bash 自带 sort、awk、grep 这些命令组合起来能做很花哨的统计分析。WSL 的情况更接近真实 Linux。你可以在 WSL 里直接访问 Windows 分区路径挂载在/mnt/c、/mnt/d下面然后运行wsl du -sh /mnt/d/data/*这个方案的好处是 du 的实现是纯正的 Linux coreutils所有参数、管道、脚本习惯都能直接复用。缺点是跨文件系统访问有性能损耗尤其是 WSL2 通过 9P 协议访问 Windows 分区时扫描大量小文件会比较慢。不过对大文件、大目录的统计来说性能还是能接受的而且胜在顺手。2.4 方案对比总表我把上面这几种方案放在一起对比方便你按自己的场景快速做选择方案类型速度上手难度适用场景备注Sysinternals du.exe命令行中等偏上低服务器、脚本自动化微软官方免安装WizTree图形界面极快秒级极低本地交互排查直接读 NTFS MFTWinDirStat图形界面慢低可视化分析文件类型开源免费TreeSize Free图形界面中等低目录树报告导出免费版够用Git Bash du命令行中等低已装 Git 的用户路径格式要适应WSL du命令行中低跨盘中等习惯 Linux 命令的用户需要先装 WSLPowerShell 脚本命令行较慢中等定制化自动化灵活但性能有限我的建议很直接本地排障用 WizTree服务器和脚本用 Sysinternals du日常想在命令行里体验原汁原味的du -h就开 Git Bash 或 WSL。3. 手写一个 PowerShell 版 du脚本讲解与参数优化3.1 基础版统计目录总大小如果你不想安装任何第三方工具只想在 PowerShell 里快速看一个目录的总大小下面的脚本就是最简版本$path D:\data $totalBytes (Get-ChildItem -Path $path -Recurse -Force -File -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum {0:N2} GB -f ($totalBytes / 1GB)这条命令的核心逻辑很简单Get-ChildItem递归取出目录下所有文件-Force把隐藏文件和系统文件也算进去-File只保留文件对象-ErrorAction SilentlyContinue把无权访问的路径错误吞掉然后Measure-Object -Property Length -Sum把所有文件的字节数加起来最后除以 1GB 转成 GB 输出。实际操作中你肯定会遇到一个坑如果目录为空或者全部路径都无权访问Measure-Object -Sum返回的结果是$null此时直接做除法会报错。稳妥的写法是加一个空值保护$totalBytes 0 Get-ChildItem -Path $path -Recurse -Force -File -ErrorAction SilentlyContinue | ForEach-Object { $totalBytes $_.Length } {0:N2} GB -f ($totalBytes / 1GB)这个版本的性能会更差一点因为一条一条累加比 Measure-Object 慢但胜在稳定。如果你只是偶尔看一下目录大小上面任意一种都够用没必要追求极限性能。3.2 增强版一键列出 Top N 大目录基础版只能看一个目录的总大小实际排障时更常用的是“列出某目录下最大的 N 个子目录”。我把这个需求封装成一个函数你可以直接复制到 PowerShell profile 里长期使用function Get-DirectorySize { param([string]$Path .) $bytes (Get-ChildItem -Path $Path -Recurse -Force -File -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum [PSCustomObject]{ Path $Path SizeBytes [int64]$bytes SizeGB [math]::Round($bytes / 1GB, 2) } } Get-ChildItem D:\data -Directory | ForEach-Object { Get-DirectorySize -Path $_.FullName } | Sort-Object SizeBytes -Descending这里有一个细节值得注意排序时我按SizeBytes而不是SizeGB排。因为多个目录的 GB 值经过四舍五入后可能相等按原始字节排序才能保证顺序精确。实际输出中你还可以在Sort-Object后面接Select-Object -First 10 -Projection只取前十个结果。脚本能跑通之后我强烈建议把它封装成一个“目录大小排行榜”工具。我在实际运维中经常需要对比同一数据目录下面几十个子项目的磁盘占用用这个函数跑一遍按大小排序输出直接就能知道哪些项目已经膨胀到需要扩容或者清理的程度非常直观。你甚至可以把它写成Get-ChildItem D:\data -Directory | ForEach-Object { Get-DirectorySize -Path $_.FullName } | Sort-Object SizeBytes -Descending | Format-Table -AutoSize这样输出的表格会非常清晰每一行是一个子目录大小从大到小排列。3.3 按文件类型归组找出谁在占空间有时候磁盘空间不是被某个目录吃掉的而是被某类文件吃掉的。比如服务器日志目录虽然分散在很多项目里但罪魁祸首都是.log文件。遇到这种场景按目录统计就失效了你得按文件扩展名归组统计Get-ChildItem -Path D:\data -Recurse -File -Force -ErrorAction SilentlyContinue | Group-Object Extension | Select-Object {nExtension;e{if ($_.Name) {$_.Name} else {(无扩展名)}}}, Count, {nSizeMB;e{[math]::Round((($_.Group | Measure-Object Length -Sum).Sum) / 1MB, 2)}} | Sort-Object {eSizeMB;Descending$true} | Select-Object -First 15这段脚本会把目录下所有文件按扩展名分组统计每组文件数量和总大小最后按大小倒序排出前十五名。运行结果会告诉你到底是.mp4文件占空间还是.bak备份文件堆积太久还是.tmp临时文件在悄悄消耗磁盘。这个功能在“磁盘持续增长”的场景下特别有用。有一次我接手一台文件服务器磁盘每个月都会多出 20 GB排查目录发现是分散在几百个用户目录下的邮件附件。后来用按扩展名归组的脚本一跑发现大头是.ost离线邮箱文件问题定位就非常快了。所以这个脚本值得好好保留。3.4 自动化与性能优化Write-Output 的 PowerShell 脚本最大的问题是性能尤其是Get-ChildItem -Recurse在大目录树下会非常慢。我实测过一个 500 GB 左右、文件数大约 30 万的文件服务器共享目录PowerShell 脚本要跑十几分钟而 Sysinternals du 只需要两三分钟WizTree 更是几秒完事。如果你坚持要用 PowerShell 做自动化巡检几个性能优化思路可以组合使用一是并行化。PowerShell 7 里可以用ForEach-Object -Parallel把顶层目录拆给多个线程分别统计再汇总结果。对于多核服务器速度提升非常明显。二是避开 PowerShell 的慢遍历。遇到特别大的目录树时我会直接用robocopy的列表模式来快速获取总字节数。robocopy 是 Windows 自带的工具对长路径、权限问题容错性极好跑一次robocopy D:\data C:\__dummy__ /L /S /NJH /BYTES日志末尾会输出文件总数和总字节数速度比 PowerShell 遍历快不少。三是混合方案。先跑一遍 WizTree 全盘扫描拿到大目录排序再对前几个大目录用 PowerShell 脚本做精确到文件级的明细统计兼顾速度与灵活性这个组合我用了很久。4. 从 Docker 和 WSL 的场景看磁盘统计的真实需求4.1 Docker Desktop 的 vhdx 膨胀怎么定位现在很多人在 Windows 上装了 Docker Desktop磁盘被吃光后打开资源管理器半天找不到原因其实很大程度上是被 Docker 的虚拟磁盘文件坑了。Docker Desktop 默认采用 WSL2 后端所有镜像、容器、数据卷都存放在一个大的虚拟磁盘文件里路径一般是C:\Users\你的用户名\AppData\Local\Docker\wsl\data\ext4.vhdx这个ext4.vhdx是 Docker 内部的 Linux 文件系统镜像Windows 资源管理器根本看不到里面的内容只知道这个文件可能高达几十 GB。想定位里面到底什么占空间用 Windows 侧的 du 类工具没有意义你必须进入 Docker 对应的 WSL 发行版或者用docker system df来看docker system df -v这条命令会列出镜像、容器、本地数据卷、构建缓存的详细占用比盲目删 vhdx 安全得多。如果发现数据卷占空间很大可以用下面的命令进入卷内看明细docker run --rm -v 卷名:/data alpine du -sh /data/*通过这个方式容器内部的数据分布就很清楚了。平时做磁盘清理时先跑docker system df判断缓存和悬空镜像的比例再对症下药不要一上来就删 vhdx否则会把有用的镜像数据也删掉。4.2 进容器里用原生 du 排查日志和数据卷实际排查容器占用时Linux 的 du 比 Windows 侧的工具都好用因为容器本来就是 Linux 环境。我的常规操作是docker exec -it 容器名 sh -c du -h --max-depth1 / | sort -hr | head -20这条命令直接进入容器从根目录开始摸清占用分布。最常见的大户有三个应用日志目录比如/var/log、包管理缓存比如 apt 缓存、以及数据库数据目录。日志目录特别容易失控很多容器不配置日志轮转一个晚上就能堆出好几个 GB。排查的时候重点看/var/lib/docker/containers下的 json 日志文件这是 docker 默认的 stdout/stderr 存储位置如果发现某容器日志已经巨大就该配置 log rotate 了。在 Windows 上装了 Docker Desktop 的读者尤其要养成定期看容器磁盘占用的习惯。Windows 宿主机的 du 类工具看不到容器内部而docker exec配合 du 是唯一顺手的路子。4.3 WSL 发行版本体占用与瘦身除了 DockerWSL 发行版本身也会在 Windows 侧生成一个ext4.vhdx文件位置形如C:\Users\你的用户名\AppData\Local\Packages\发行版包名\LocalState\ext4.vhdx这个文件也会随着使用不断膨胀。问题在于 WSL 里的删除操作通常不会让 vhdx 自动缩小文件系统内部删了 20 GB外面的 vhdx 可能仍然是原来的大小。想确认 WSL 发行版内部哪里占空间直接在 WSL 里用 Linux du 就行了du -h --max-depth1 / | sort -hr确认清理干净后要让 vhdx 真正瘦身需要一步步操作。先关闭 WSLwsl --shutdown然后通过 diskpart 压缩虚拟磁盘diskpart select vdisk fileC:\Users\你的用户名\AppData\Local\Docker\wsl\data\ext4.vhdx attach vdisk readonly compact vdisk detach vdisk exit这里需要注意的是compact 之前必须保证 WSL 已经关闭否则磁盘被占用会报错。压缩虚拟磁盘的原理是把 vhdx 内部空闲块释放回宿主文件系统所以只有 WSL 内部先删除文件压缩才有明显效果。这一步做完C 盘经常能多出十几个 GB 空间。5. 常见问题与排查技巧实录5.1 统计结果和资源管理器对不上差在哪很多人第一次用 du 类工具或脚本第一反应都是“这个数字怎么和资源管理器属性里显示的不一样”。这里面的差异通常来自四个原因。第一是隐藏文件和系统文件。资源管理器默认不显示隐藏文件属性统计时不会把它们算进去但du -force和 WizTree 这类工具会算。比如一个目录下藏着大量.git目录资源管理器那个数字就会明显偏小。第二是回收站和卷影副本。回收站里的文件大小不计入源目录但残留的卷影副本VSS 快照可能占着磁盘空间却在你统计的目录里看不到。第三是硬链接和符号链接。NTFS 硬链接会让同一份数据被多个目录引用脚本按文件名累计时会重复计数。第四是稀疏文件和压缩文件。文件逻辑大小和实际占用磁盘的物理大小不同很多统计工具默认显示逻辑大小而资源管理器属性在某些版本里显示的是“大小”而不是“占用空间”。所以我的习惯是不纠结于到底哪个数字绝对准确而是看趋势和粒度。你只需要知道哪些目录增长最快哪个目录是主要矛盾至于 5 GB 以内的偏差大多来自上述系统机制不影响判断。5.2 一堆 Access Denied 要不要理会用 PowerShell 跑全盘统计时经常会刷出一堆“拒绝访问”的错误集中在System Volume Information、C:\Windows\System32\config这类受系统保护目录。出现这些提示是正常的哪怕是管理员账户在没有 SYSTEM 权限的情况下也无法读取那些系统专用目录。我的建议是只要报错目录不属于你要分析的业务数据目录直接用-ErrorAction SilentlyContinue忽略掉就行不用特意去修改 NTFS 权限。强行拿takeown或者icacls改系统目录权限很容易把系统搞出问题得不偿失。如果你确实需要统计包含系统目录在内的全盘数据那就用管理员权限运行工具或者用 Sysinternals du 这类以管理员令牌运行的工具但依然会有部分系统路径被跳过这属于正常情况。5.3 脚本闪退、乱码、执行策略被拦怎么破Windows 上跑 PowerShell 脚本最常见的问题有三个我一个个排过都有解。第一个是“在此系统上禁止运行脚本”报错这是 PowerShell 执行策略默认 Restricted 导致的。解决办法是在当前用户级别放行执行Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned或者运行时临时绕过限制powershell -ExecutionPolicy Bypass -File your-script.ps1第二个问题是中文路径乱码。这个通常不是脚本本身的问题而是输出重定向时编码不对。PowerShell 5.1 默认输出到文件用的是系统 ANSI 编码遇到 UTF-8 简体中文路径会变成乱码。解决办法是在脚本里把输出改成 UTF-8[Console]::OutputEncoding [System.Text.Encoding]::UTF8或者Out-File -Encoding utf8。第三个问题是脚本跑到一半直接闪退或者卡死多见于大目录递归遍历。闪退多半是因为内存被大量对象撑爆PowerShell 在处理几十万文件时会生成海量对象极其吃内存。解决思路是分段统计、及时释放变量或者干脆换 Sysinternals du / WizTree 来做大规模扫描。卡死则往往不是因为死循环而是因为某个网络盘或 U 盘处于不健康状态Get-ChildItem在等待 I/O 超时这种情况可以加一个超时机制或者把网络映射盘排除在统计范围之外。5.4 长路径、符号链接、硬链接这些坑Windows 的老版本限制了路径最大长度是 260 个字符现在的 Windows 10/11 虽然支持长路径但默认没有打开。如果你用 PowerShell 统计一个深层次的目录路径超过了 MAX_PATH脚本就会报DirectoryNotFoundException。解决办法有两个一是在注册表里开启长路径支持设置HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled为 1 并重启二是用\\?\前缀给路径加上 Win32 文件命名空间的转义。Sysinternals du 对长路径的处理相对好一些这也是我推荐它做服务器统计的原因之一。符号链接和 Junction 链接则是另一个隐蔽陷阱。目录里如果有一个 Junction 指向了 C 盘你在统计 D 盘时可能会把它当作普通目录递归进去导致统计结果虚高甚至出现递归死循环。PowerShell 里面判断链接目录的方法是检查Attributes是否包含ReparsePointGet-ChildItem -Path $path -Directory | Where-Object { $_.Attributes -notmatch ReparsePoint }WSL 和 Git Bash 里的 Linux du 默认不跟随符号链接但 Windows 的 Junction 在某些挂载方式下会被当作普通目录遍历这点也要留意。5.5 问题速查表症状可能原因处理方法“du 不是内部或外部命令”Windows 没有原生 du装 Git/WSL/Sysinternals du 或改用 PowerShell 脚本统计结果比资源管理器大隐藏文件、硬链接被计入用 -Force 确认明细或参考物理占用而不是逻辑大小统计结果比资源管理器小权限拒绝、受保护目录被跳过管理员权限运行工具必要时接受遗漏大量 Access Denied无 SYSTEM 权限不用管只能统计你有权限的部分脚本运行报安全错误PowerShell 执行策略限制Set-ExecutionPolicy 或 Bypass脚本输出乱码编码不匹配强制设置 UTF-8 输出遍历时卡死网络盘 I/O 或目录树过大排除网络盘、分段统计、换工具路径找不到/超长超过 260 字符路径限制开 LongPathsEnabled 或用 \?\ 前缀统计里出现几百 GB 的虚拟文件vhdx 是 Docker/WSL 的虚拟磁盘用 docker system df 进内部排查再考虑 compact我再分享一个自己的习惯组合日常交互排障先开 WizTree几秒钟扫完整个 C 盘写巡检脚本和定时任务时用 PowerShell 封装的统计函数把结果输出成 CSV 丢给图表遇到 Docker 或 WSL 的 vhdx 膨胀就先docker system df定位内部占用再按照压缩步骤瘦身。这套流程用了好几年基本覆盖了 Windows 磁盘统计的绝大多数需求。最后一个小技巧不管用哪种工具统计请记得先确认你是不是管理员很多“漏算”“报错”并不是工具的问题而是权限不够。把权限提到管理员级别再跑结果会准很多排查思路也会清晰得多。