
在日常开发中真正让人烦躁的往往不是某个复杂系统的高深设计而是一连串细碎的重复劳动批量任务跑起来像黑盒不知道还要等多久一堆命名混乱的文件摆在面前却不敢批量改名排查线上问题时明明日志就在手边却只能靠肉眼一行一行找关键词。这些问题单独看都很小但每天都在打断工作流累积起来非常消耗注意力。我选择用 Python 标准库写三个小工具来应对这些场景一个带进度反馈的命令行进度条、一个带安全保护机制的批量文件重命名脚本、一个按关键词统计日志出现次数的命令行工具。我给它们起了个很随意的名字——可爱的三小只因为它们都很小但各自稳稳地解决一个具体问题。本文会讲清楚三件事这三个小工具分别解决什么痛点、完整代码怎么写、如何组合成一个统一命令行入口。同时也会把运行验证、常见坑位和工程化建议一并给出。如果你经常写脚本处理文件或日志这篇文章可以直接照着用。1. 这三个小工具到底解决了什么问题1.1 脚本不是黑盒需要进度反馈很多人写耗时脚本时只在开头打印一句“开始处理”然后在最后打印一句“处理完成”。中间过程完全不可见。如果任务需要几分钟甚至更久你只能盯着屏幕等或者切去干别的然后过一会回来再看是否跑完。更麻烦的是你无法确定脚本是否卡住了。进度条解决的不只是“等待时好看一点”而是让程序状态变得可见。你能看到当前处理到第几条、完成百分比是多少、大概还要多久。这对长时间运行的批量任务尤其重要。手动写一个进度条并不难但它需要处理一些细节同一行内刷新、终端宽度适配、无终端环境下的降级。1.2 批量重命名不能靠手写循环批量重命名文件是高频需求。比如把文件名中的空格换成下划线、去掉某些前缀、按规则统一编号。很多人第一反应是写一个 Python 循环用os.rename一个一个改。问题在于这种临时脚本非常容易出事没有预演机制脚本一跑就直接改文件名改错了很难恢复。没有冲突检测如果新文件名已经存在os.rename在某些平台会直接覆盖已有文件。正则写错时可能把一批本来正常的文件改得面目全非。批量重命名工具必须默认“安全优先”。先以 dry-run 模式列出所有将要发生的变化确认无误后再真正执行。这个习惯可以避免绝大多数文件操作事故。1.3 查日志不能只靠肉眼翻排查 Bug 时最常见的第一步是统计日志里某个关键词出现了多少次比如ERROR、TimeoutException、某个请求 ID。日志文件小的时候还行几十 MB 甚至几百 MB 时用编辑器打开搜索会非常卡。虽然生产环境有 ELK、Loki 这类专业日志系统但对于本地日志、临时抓取的日志片段、或者不想搭整套平台的场景一个小型关键词统计脚本更直接。它能流式读取大文件不占太多内存并输出每个关键词的出现次数排序结果。2. 基础概念与设计思路2.1 标准库优先小工具不等于引入重型依赖这三个小工具全部基于 Python 标准库实现没有安装任何第三方依赖。你可能会问进度条用tqdm不就行了吗文件改名也有现成工具日志分析有成熟的日志平台。为什么还要自己写因为标准库版本有三个优势在任何装有 Python 的机器上都能跑不需要额外安装依赖。代码量小逻辑透明出问题一眼能看懂。可以按自己的需求定制比如重命名工具默认 dry-run日志统计支持多关键词和编码参数。当然这不意味着所有场景都应该自己造轮子。如果项目里已经用了tqdm直接用即可如果公司有完善的日志分析平台也不需要自己统计。本文的小工具适合“轻量、快速、临时”的场景。2.2 输入输出与错误处理约定在设计这三个小工具时我统一了输入输出约定工具输入输出安全机制进度条可迭代对象、描述文字同一行动态刷新非终端环境自动去掉颜色批量重命名文件夹路径、正则、替换串变更清单默认 dry-run检查目标冲突日志统计文件或目录、关键词列表关键词次数排序流式读取忽略不可读文件统一的约定让代码更清晰也方便后续扩展。2.3 关键技术点这三个工具分别依赖三个重要的技术点进度条依赖回车符\r和终端宽度检测。\r能把光标移到行首从而在同一行内刷新内容。批量重命名依赖正则表达式re.subn它不仅能替换还能返回替换次数方便判断哪些文件需要改名。日志统计依赖流式读取和正则多关键词匹配。逐行读取可以避免一次性把大文件加载进内存。3. 环境准备与项目结构3.1 环境要求本文代码基于 Python 3.9 及以上版本使用标准库编写不需要额外安装依赖。在 Windows、macOS、Linux 上都可以运行。建议先创建一个示例目录来测试。mkdir cute-tools-demo cd cute-tools-demo如果你希望隔离环境可以创建虚拟环境python -m venv .venv source .venv/bin/activate # Linux / macOS # 或 .venv\Scripts\activate # Windows3.2 项目目录结构完整的项目结构如下cute-tools-demo/ ├── cute_tools/ │ ├── __init__.py │ ├── progress_tool.py │ ├── rename_tool.py │ ├── logcount_tool.py │ └── main.py └── tests/ └── test_tools.py如果你只是临时使用也可以把几个.py文件放在同一目录下直接运行。但使用包结构能让后续扩展和测试更方便。3.3 运行方式说明由于项目使用了包结构运行统一通过模块方式python -m cute_tools.main progress --times 5 python -m cute_tools.main rename ./demo --pattern \s --replacement _ python -m cute_tools.main logcount app.log ERROR WARN接下来我们逐个实现这三个小工具。4. 第一只Python 彩色进度条4.1 进度条的实现原理进度条的本质是在一次循环中把当前进度百分比转化为填充长度然后通过\r回到当前行首重新输出一行覆盖旧内容。为了让进度条在终端里更直观可以用 ANSI 转义序列给内容上色。ANSI 颜色序列形如\033[32m将后面的文字变成绿色直到出现\033[0m恢复默认颜色。不过有个坑Windows 传统控制台默认不支持 ANSI 转义序列。需要调用 Windows API 开启虚拟终端处理能力。代码里通过ctypes调SetConsoleMode实现这样在 Windows 10 的终端中也能正常显示颜色。还有一个细节如果输出被重定向到文件就不应该输出颜色码否则文件里会出现一堆[32m这样的垃圾字符。判断方法是sys.stdout.isatty()只有标准输出连接到终端时才启用颜色。4.2 代码实现# 文件路径cute_tools/progress_tool.py import os import sys import time import shutil GREEN \033[32m RESET \033[0m def enable_vt100(): 在 Windows 10 下启用 ANSI 转义序列支持。 if os.name nt: import ctypes kernel32 ctypes.windll.kernel32 kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7) def progress_bar(iterable, totalNone, descProgress, bar_lengthNone): 生成器包装器迭代时可显示进度条。 if total is None: try: total len(iterable) except TypeError: total 0 if bar_length is None: columns, _ shutil.get_terminal_size((80, 20)) bar_length max(20, min(60, columns - len(desc) - 15)) use_color sys.stdout.isatty() for idx, item in enumerate(iterable, start1): yield item if total 0: percent idx / total else: percent 0.0 filled int(bar_length * percent) bar # * filled - * (bar_length - filled) if use_color: sys.stdout.write( f\r{GREEN}{desc}: [{bar}]{RESET} {percent:6.1%} ({idx}/{total}) ) else: sys.stdout.write(f\r{desc}: [{bar}] {percent:6.1%} ({idx}/{total})) sys.stdout.flush() sys.stdout.write(\n) def run_demo(times10, interval0.2): 演示进度条效果。 for _ in progress_bar(range(times), descSleepDemo): time.sleep(interval)4.3 关键逻辑讲解progress_bar是一个生成器函数。它先把传入的iterable包住每次迭代时先yield item让业务逻辑执行然后再更新进度条。这里的yield是核心设计它允许你把进度条嵌入到任意for循环里而不需要修改业务代码的结构。终端宽度通过shutil.get_terminal_size获取。进度条长度会限制在 20 到 60 个字符之间避免在窄终端里换行。如果total无法获取比如传入的是生成器进度条会退化为只显示当前索引不显示百分比。这虽然不是最好的体验但至少不会报错。4.4 调用示例python -m cute_tools.main progress --times 5 --interval 0.3运行后屏幕上同一行会动态刷新最终效果类似SleepDemo: [#####---------------] 40.0% (2/5)5. 第二只批量文件重命名小工具5.1 为什么需要安全机制批量重命名最大的风险是“不可逆”。文件名不像代码版本多数人不会为文件名做版本管理。一旦批量改错几乎只能手动恢复。因此这个工具的设计原则是默认只列出变更不实际改名。必须加上--apply参数才真正执行。如果新文件名已经存在跳过而不是覆盖。支持正则表达式灵活匹配旧文件名。5.2 代码实现# 文件路径cute_tools/rename_tool.py from __future__ import annotations import os import re from typing import List, Tuple RenameResult Tuple[List[Tuple[str, str, str]], List[Tuple[str, str, str]]] def bulk_rename( folder: str, pattern: str, replacement: str, apply: bool False, ) - RenameResult: 批量重命名文件