在日常开发中,真正让人烦躁的往往不是某个复杂系统的高深设计,而是一连串细碎的重复劳动:批量任务跑起来像黑盒,不知道还要等多久;一堆命名混乱的文件摆在面前,却不敢批量改名;排查线上问题时,明明日志就在手边,却只能靠肉眼一行一行找关键词。这些问题单独看都很小,但每天都在打断工作流,累积起来非常消耗注意力。
我选择用 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, total=None, desc="Progress", bar_length=None): """生成器包装器,迭代时可显示进度条。""" 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, start=1): 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(times=10, interval=0.2): """演示进度条效果。""" for _ in progress_bar(range(times), desc="SleepDemo"): 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: """批量重命名文件