# 目录空间占用查询 **Repository Path**: cloudczheng/dir_size_check ## Basic Information - **Project Name**: 目录空间占用查询 - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-11 - **Last Updated**: 2026-09-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: ai生成项目, 桌面应用 ## README # 目录容量扫描器 > **AI 生成声明**:本项目的全部源代码与文档均由 AI 生成(AI-generated),由 **CloudCZh** 提出需求、验收与维护。 一个用 Python 标准库写成的目录容量扫描工具:递归统计指定目录下每个子文件夹占用的空间,按大小排序输出,并带实时进度显示。 无第三方依赖,零安装成本。 --- ## 目录 - [特性](#特性) - [环境要求](#环境要求) - [快速开始](#快速开始) - [参数说明](#参数说明) - [输出示例](#输出示例) - [性能](#性能) - [技术实现](#技术实现) - [常见问题](#常见问题) - [已知限制](#已知限制) - [文件清单](#文件清单) - [许可证](#许可证) - [作者](#作者) --- ## 特性 | 特性 | 说明 | |---|---| | 递归统计 | 统计每个子文件夹的**实际字节数**与文件数量 | | 多种排序 | 按体积 / 按文件数 / 按名称 | | 图形选目录 | 双击即可弹出 Windows 原生「选择文件夹」对话框,不用记路径 | | 拖拽即扫 | 把文件夹拖到 `scan_dir.bat` 上,直接扫描 | | 实时进度 | 扫描过程中在原地刷新「当前目录 + 当前文件名」 | | 结束分支菜单 | 扫完可选:退出 / 重新选择目录 / 重新扫描 | | 多目录 | 一次扫描多个目录 | | 条形分布图 | 终端内用 `█` 直观展示占比 | | 导出 | 支持 JSON 与 CSV(CSV 带 BOM,Excel 打开不乱码) | | 安全 | 默认不跟随符号链接与目录联接,避免重复计算和死循环 | | 容错 | 无权限的目录跳过并计数,不会让扫描崩掉 | | 中文友好 | 中英文混排表格按显示宽度对齐,不会错位 | --- ## 环境要求 - **Python 3.8+**(已在 3.11 与 3.13 上验证) - **仅使用标准库**:`argparse`、`collections`、`csv`、`ctypes`、`datetime`、`json`、`os`、`shutil`、`stat`、`string`、`sys`、`threading`、`unicodedata` - 操作系统:Windows 优先(做了大量 Windows 专项处理),Linux / macOS 亦可运行 > **关于图形目录选择框**:它依赖 `tkinter`。部分精简版或嵌入式 Python 发行版没有编译 `tkinter`,此时程序会自动降级为终端选择器,并打印提示说明原因。Windows 官方版 Python 默认自带。 --- ## 快速开始 ### 方式一:双击 / 拖拽(推荐) 把 `dir_size_scan.py` 和 `scan_dir.bat` 放在同一个文件夹里: - **双击 `scan_dir.bat`** → 弹出目录选择窗口 - **把文件夹拖到 `scan_dir.bat` 上** → 直接扫描该文件夹,支持一次拖多个 启动器会自动挑选可用的 Python:优先选择带 `tkinter` 的解释器(保证有图形弹窗),找不到再退回任意可用 Python。 ### 方式二:命令行 ```bash python dir_size_scan.py # 不带参数 -> 弹出目录选择器 python dir_size_scan.py . # 扫描当前目录 python dir_size_scan.py C:\Users # 扫描指定目录 python dir_size_scan.py C:\ D:\ E:\ # 一次扫描多个目录 ``` --- ## 参数说明 ``` usage: dir_size_scan.py [-h] [-n N] [-s SIZE] [-j JOBS] [--sort {size,count,name}] [--cli-pick | --gui-pick] [--follow-links] [--no-bar] [--no-loose] [--json FILE] [--csv FILE] [-q] [--menu | --no-menu] [paths ...] ``` ### 位置参数 | 参数 | 说明 | |---|---| | `paths` | 要扫描的目录,可以给多个。不给则进入目录选择流程 | ### 选项 | 参数 | 默认 | 说明 | |---|---|---| | `-h, --help` | | 显示帮助 | | `-n, --top N` | `0` | 只显示前 N 项,`0` 表示全部 | | `-s, --min-size SIZE` | | 过滤掉小于该体积的文件夹,支持 `100M` / `1G` / `1024` 等写法 | | `-j, --jobs N` | `4` | 并发线程数。实测 4 是最优拐点,8 以上因锁竞争反而变慢 | | `--sort {size,count,name}` | `size` | 排序依据:体积 / 文件数 / 名称 | | `--cli-pick` | | 强制使用终端选择器(不弹图形框) | | `--gui-pick` | | 强制使用图形选择框 | | `--follow-links` | 关 | 跟随符号链接与目录联接。可能重复计算,慎用 | | `--no-bar` | 关 | 不显示分布条形图 | | `--no-loose` | 关 | 不统计根目录下散落的文件 | | `--json FILE` | | 导出 JSON | | `--csv FILE` | | 导出 CSV(UTF-8 with BOM) | | `-q, --quiet` | 关 | 不显示扫描进度 | | `--menu` | | 强制显示结束后的询问菜单 | | `--no-menu` | | 扫完直接退出,不显示询问菜单 | > `--cli-pick` / `--gui-pick` 互斥,`--menu` / `--no-menu` 互斥。 ### 常用组合 ```bash python dir_size_scan.py C:\Users -n 20 # 只看前 20 名 python dir_size_scan.py D:\ -s 1G # 只看大于 1GB 的 python dir_size_scan.py C:\ --sort count # 按文件数量排序(找出海量小文件的目录) python dir_size_scan.py C:\ --json out.json # 导出 JSON python dir_size_scan.py C:\ --csv out.csv # 导出 CSV python dir_size_scan.py C:\ --no-menu > report.txt # 输出重定向到文件,不弹菜单 ``` --- ## 输出示例 ### 扫描过程 扫描时会在终端底部**原地刷新**一行进度,显示当前正在读取的目录与文件名: ``` ================================================================== 扫描路径:C:\Users\Administrator\.workbuddy ================================================================== 扫描中 [26/28] 39,775 个文件 …uddy\binaries\python\envs\default\Lib\site-packages\executing\py.typed 耗时:0.32 秒 并发线程:4 排序:size ``` > 上面是为了展示把每一帧拆开显示的效果,实际屏幕上只有一行在不断变化。 ### 扫描结果 ``` # 文件夹 大小 文件数 占比 分布 ------------------------------------------------------- 1 方正审校 945.53 MB 8,127 58.6% ██████████████████████ 2 Tencent Files 611.85 MB 3,644 37.9% ██████████████········ 3 MuMu共享文件夹 35.68 MB 7 2.2% █····················· 4 Banished 11.54 MB 1 0.7% ······················ ------------------------------------------------------- 合计 1.58 GB 11,841 共 4 个文件夹条目。 ``` `<根目录下的散落文件>` 是一个特殊条目,统计的是直接躺在被扫描目录里、不属于任何子文件夹的文件——排查磁盘占用时这类文件经常是漏网之鱼。 ### 结束菜单 ``` ------------------------------------------------------------------ 扫描完成,接下来要做什么? [0] 退出程序 [1] 重新选择目录 [2] 重新扫描 请输入选项,直接回车 = 退出 > ``` 直接回车等同于退出。当标准输入不是真实控制台时(管道、重定向),这个菜单会自动跳过。 --- ## 性能 测试环境:Windows,12 逻辑核,SSD。每组数据取 3 次运行的最小值。 ### 并发加速 | 目录 | 规模 | 单线程 | 默认 4 线程 | 加速比 | |---|---|---|---|---| | `.workbuddy` | 46,942 文件 / 8,210 目录 | 0.55 s | 0.28 s | **2.0x** | | `Android` | 44,197 文件 / 16,806 目录 | 1.10 s | 0.51 s | **2.2x** | | `Programs` | 7,079 文件 | 0.11 s | 0.04 s | **2.6x** | | `.nuget` | 18,512 文件 / 11,184 目录 | 0.65 s | 0.66 s | 持平 | ### 为什么默认是 4 线程 | 线程数 | `.workbuddy` | `Android` | |---|---|---| | 1 | 0.55 s | 1.10 s | | 2 | 0.31 s | 1.09 s | | **4** | **0.28 s** | 0.51 s | | 8 | 0.36 s | 0.44 s | | 16 | 0.41 s | 0.57 s | 超过 4 个线程后,共享任务队列的锁竞争开销超过了并行收益,整体反而变慢。可以用 `-j` 按自己的磁盘情况微调:**机械硬盘建议 2,SSD 建议 4**。 > `.nuget` 之所以没有加速,是因为它的目录结构极其扁宽(11,184 个目录里平均只有 1.6 个文件),单位任务太小,调度开销吃掉了全部收益。 --- ## 技术实现 ### 并行粒度是「单个目录」而不是「顶层分支」 这是本项目最关键的一个设计决定。 早期版本用线程池把**顶层子目录**预先切分,一个线程负责一个分支。听起来合理,但实际上分支数常常只有一两个: - `.nuget` 顶层只有 `packages` 一个目录 - `Android` 顶层只有 `Sdk` 一个目录 这种情况下无论给多少线程,全部工作都压在同一个线程上,**实测 16 线程的加速比是 1.00x**。 现在的实现改为共享的目录队列:所有目录进同一个 `collections.deque`,谁空谁取,粒度细到单个目录。任何形状的目录树都能把线程吃满。同时每个目录都记录着自己属于哪个顶层分支,所以报告里的分支小计依然准确。 ### 不跟随符号链接与目录联接 Windows 的目录联接(junction)会让递归遍历重复计算同一批文件,严重时**死循环**。要注意的是,联接调用 `is_symlink()` 返回的是 `False`,只判断符号链接是不够的,还必须检查文件属性: ```python attrs & stat.FILE_ATTRIBUTE_REPARSE_POINT ``` 被跳过的链接会单独计数,在报告头部提示。 ### 进度显示不拖慢扫描 直观写法是每读一个文件就刷一次屏,但遇到十几万文件的目录时,光是终端输出就能把扫描速度拖垮,画面也会闪得看不清。现在的做法是: - 工作线程只更新共享状态,且**每 64 个文件才加一次锁**(`PUBLISH_EVERY`) - 由一个后台线程每 0.12 秒重绘一次那一行,用 `\r` 原地覆盖 行宽严格限制在 `终端列数 - 1` 以内——**必须减 1**,否则行尾顶到最后一列会让控制台自动换行,`\r` 覆盖就会失效,画面滚成一团。路径过长时从尾部截取(文件名最有信息量),前面补 `…`。 只有标准输出接了真实控制台时才会启用进度行;重定向到文件时完全不输出,不会往里掺 `\r` 垃圾。 ### Windows 上的 `NUL` 陷阱 Windows 的 `NUL` 设备是字符设备,`sys.stdin.isatty()` 会对它返回 `True`。这意味着用 `< NUL` 或 `< /dev/null` 重定向时会误判成"有人在交互",照样弹出菜单。 解决办法是再用控制台 API 复核一次: ```python GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &mode) ``` 它对管道和 `NUL` 都失败,只对真实控制台成功。 ### 终端兼容性 - 中文目录名按显示宽度对齐(用 `unicodedata.east_asian_width()` 判断),`str.ljust()` 遇到中文会错位 - 输出编码不足时,条形图字符自动从 `█` / `·` 降级为 `#` / `-` - Windows 控制台编码不是 UTF-8 时主动纠正 --- ## 常见问题 **Q:为什么没有弹出图形选择框?** 当前 Python 没有编译 `tkinter`。程序会打印提示并自动降级为终端选择器。想用弹窗可以: - 双击 `scan_dir.bat`(启动器会优先挑带 `tkinter` 的解释器) - 或显式指定:`py -3 dir_size_scan.py`、`E:\Python3.11\python.exe dir_size_scan.py` **Q:扫出来的数字和「属性」里看到的不一样?** 这是正常的,原因包括: - 本工具统计的是**文件逻辑字节数之和**,不反映磁盘实际占用(不计算簇大小) - Windows 资源管理器会跳过一部分隐藏/系统文件 - 本工具默认**不跟随**符号链接和目录联接,其指向的内容不计入 - 硬链接会被重复计算(见下方「已知限制」) **Q:有文件夹没被统计进去?** 两类情况会被跳过并单独计数:无权限访问的目录、符号链接与目录联接。报告头部会显示跳过项数。 **Q:同一目录两次扫描结果不一样?** 该目录正在被写入。典型场景是日志目录——扫描期间程序还在往里写日志,体积自然会变。做对比测试时请选静态目录。 **Q:中文显示乱码?** 在 `cmd` 里先执行 `chcp 65001` 切到 UTF-8。用 `scan_dir.bat` 启动时已经自动设置好了。 **Q:觉得还是慢?** 试试 `-j 2`(机械硬盘)或确认目标目录是否在被杀毒软件实时扫描。另外 `-q` 可以完全关掉进度显示。 --- ## 已知限制 - **硬链接会被重复计算**:同一个文件的多个硬链接会按文件大小各计一次。`C:\Windows\WinSxS` 大量使用硬链接,扫出来的数字会明显偏大 - **不反映磁盘占用**:统计的是文件逻辑大小,不含簇对齐、NTFS 压缩、稀疏文件等因素 - **默认跳过链接**:符号链接与目录联接指向的内容不计入(可用 `--follow-links` 开启,但有重复计算与死循环风险) - **无权限目录会被跳过**:某些系统目录需要管理员权限才能读取 - **不跨文件系统去重**:多个目录指向同一物理位置时会被各算一次 --- ## 文件清单 | 文件 | 说明 | |---|---| | `dir_size_scan.py` | 主程序,单文件,仅依赖标准库 | | `scan_dir.bat` | Windows 启动器,支持双击与拖拽;自动探测可用的 Python | | `README.md` | 本文档 | | `LICENSE` | MIT 许可证 | `scan_dir.bat` 的内容是**纯 ASCII** 的,这是刻意为之:`cmd` 会用系统 ANSI 代码页读取批处理文件,写入中文会导致乱码。 --- ## 许可证 本项目基于 **MIT License** 开源,可自由使用、修改、分发,包括商业用途。 ``` MIT License Copyright (c) 2026 CloudCZh Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` 完整文本见 [LICENSE](LICENSE)。 --- ## 作者 | | | |---|---| | **作者** | CloudCZh | | **邮箱** | wfzhengyunhai@foxmail.com | | **许可证** | MIT | | **代码生成** | AI 生成(AI-generated),由作者验收与维护 | ---
**本项目全部代码与文档由 AI 生成** · MIT Licensed · © 2026 CloudCZh