
你有没有遇到过这种崩溃时刻下载文件夹里堆了两千个文件一份合同放进去就再也找不出来月底要把五个不同版本的 Excel 粘成一张总表复制粘贴到怀疑人生明明只是给文件重命名却在几十个“新建文档23.docx”里来回筛选。我全经历过。真正让我下决心写 Python 自动化脚本 的是有一次手动备份项目资料重复劳动了快两个小时最后还是漏掉了两张关键截图。第二天我花了一个周末把这些零碎需求整理成脚本其中有 10 个到现在都在我的电脑上稳定运行。这篇文章不打算讲高深理论就从我自己踩过的坑出发把这 10 个脚本分别解决什么问题、核心代码怎么搭、实际运行中会遇到哪些问题全部拆开给你看。无论你是刚学 Python 不久的新手还是已经写过一点小脚本、想系统提升效率的开发者读完都应该能直接改出一套属于自己的自动化工具箱。1. 先把框架搭明白这10个脚本是怎么选出来的1.1 判断要不要自动化只看三个标准很多朋友问我为什么偏偏是这 10 个脚本而不是别的我说一个总原则判断一件事该不该用脚本替代不看它技术含量高不高只看它是不是同时满足三个特征——高频、重复、有规律。高频指的是你一周至少会做一次重复指的是每次操作步骤几乎一模一样有规律指的是可以明确写成 if/else 或 for 循环。举个例子“把下载文件夹里的图片挪到图片目录”这种事完全符合这三条。而“帮老板设计一张年会海报”虽然也烦但你没办法用一个固定模板解决就不适合硬塞给脚本。当初我给自己列过一个“手动劳动清单”把每天、每周、每月重复做的事全写在纸上然后用这三个标准筛选了一遍最终挑出 10 个最值得写的方向。下面这张表就是我当时的选题清单序号脚本方向主要解决什么问题1下载目录自动整理文件堆成山找不到素材2批量重命名文件名混乱无法排序归档3PDF 合并与水印多份扫描件需要合成一份4商品价格监控手动刷新网页看价格变动5每日天气播报给家里群发一条天气提醒6网页正文提取把长文保存为本地笔记7Excel 多表汇总多个报表合成总表8定时邮件发送每周自动汇报进度9合同批量生成模板套用大量重复文件10磁盘清理与巡检释放空间预防磁盘爆满不夸张地说第一版脚本写得还很粗糙但随着我持续迭代每个都能稳定省下十几分钟到半小时。一个月累积下来多出来的时间相当可观。1.2 环境准备把依赖一次性装齐动手之前先建一个独立虚拟环境非常有必要。虚拟环境可以避免你之后用 pip 装不同版本库时互相打架也能让脚本运行环境更干净。Windows 和 macOS/Linux 通用操作如下python -m venv auto_env # Windows 激活 auto_env\Scripts\activate # macOS/Linux 激活 source auto_env/bin/activate激活之后再安装本文涉及的第三方库pip install pandas openpyxl pypdf requests beautifulsoup4 schedule send2trash python-docx docxtpl这几个库的含义和分工我先简单说一遍pandas和openpyxl用来处理 Excel 表格pypdf负责 PDF 合并拆分requests加上beautifulsoup4用来抓网页信息schedule是轻量级的定时任务工具send2trash可以把文件移动到回收站而不是永久删除python-docx和docxtpl用于操作 Word 和渲染合同模板。标准库里的os、shutil、pathlib、smtplib、email、logging等不需要额外安装。第一次接触的同学不要被这么多名字吓到你不需要全部记住用到哪个再回来看就行。1.3 写脚本前先做一件事把人工操作流程写出来我踩过最大的坑就是拿到需求直接写代码。后来发现越是赶时间越应该先用手写一遍操作流程。比如“整理下载文件夹”人工操作其实是这样几件事打开文件夹、按文件类型分堆、把图片放到图片目录、把文档放到文档目录、遇到同名文件要重命名不能覆盖。这些步骤翻译成代码时就对应着“遍历目录、判断后缀名、创建目录、移动文件、检查重名”。你看只要把人的流程写清楚了代码只是翻译工作。如果你现在也准备给自己的重复劳动写脚本建议先花五分钟手记操作步骤再开始敲键盘。这个习惯帮我省了无数调试时间。2. 文件与文档处理最容易被解救的三件事2.1 下载目录自动整理脚本一文件分类器电脑里的下载文件夹大概是现代人“数字杂物间”最形象的比喻。我之前有一次要找一份 PDF 合同硬是从上千个文件里翻了一刻钟最后发现它的文件名就叫“文档 (12).pdf”差点崩溃。后来我用 Python 写了这个文件分类器每次下载文件夹变得混乱只需运行一次几秒钟就能把所有文件按类型归位。核心逻辑并不复杂遍历目标目录下的每个文件通过后缀名判断它属于哪一类然后移动到对应的分类文件夹。from pathlib import Path import shutil DOWNLOAD_DIR Path.home() / Downloads RULES { 图片素材: [.jpg, .jpeg, .png, .gif, .webp], 办公文档: [.docx, .doc, .xlsx, .xls, .pptx, .pdf, .txt], 压缩包: [.zip, .rar, .7z, .tar, .gz], 安装程序: [.exe, .msi, .dmg, .deb, .rpm], } def classify(file_path: Path): suffix file_path.suffix.lower() for folder, exts in RULES.items(): if suffix in exts: return folder return 其他文件 def organize(): for item in DOWNLOAD_DIR.iterdir(): if item.is_file(): category classify(item) target_dir DOWNLOAD_DIR / category target_dir.mkdir(exist_okTrue) shutil.move(str(item), str(target_dir / item.name)) print(f移动{item.name} → {category}) if __name__ __main__: organize()运行之后下载目录里会自动多出几个分类文件夹所有文件按扩展名归位。我自己用的时候发现一个大坑如果目标文件夹里已经有同名文件shutil.move会直接覆盖非常危险。我的改进方案是当目标文件已存在时给新文件加上当前时间戳再移动这样既能保留两份文件又不会互相覆盖。你可以在移动前加一个判断target target_dir / item.name if target.exists(): timestamp time.strftime(%Y%m%d%H%M%S) target target_dir / f{item.stem}_{timestamp}{item.suffix}这个脚本我基本每周跑一次配合系统定时任务之后下载目录长期保持清爽搜索文件的效率高了很多。2.2 批量重命名工具脚本二加序号、加日期给文件重命名这件事听起来不起眼但真碰到几百张照片、几十份扫描件时手动改名能把手点到抽筋。比如我有个习惯每周把项目截图按“项目名_日期_序号”归档最开始纯手动后来写了个脚本秒级完成。这个脚本做得挺直接遍历文件夹里的所有文件按照你指定的前缀、日期和序号重新生成文件名。from pathlib import Path from datetime import date folder Path(D:/待整理文件) prefix 项目资料 for i, file in enumerate(folder.iterdir(), start1): if file.is_file(): new_name f{prefix}_{date.today().isoformat()}_{i:03d}{file.suffix} dest file.parent / new_name if not dest.exists(): file.rename(dest) print(f重命名{file.name} → {new_name})这里的i:03d把序号格式化成三位数比如 001、002方便排序。日期用date.today().isoformat()输出能直接排进文件名体系里。用过几轮之后我发现最值得留意的是特殊字符。Windows 上文件名不能包含\ / : * ? |这些符号如果原文件名里混有它们rename会直接报错。我自己会在重命名前做一个过滤函数把所有非法字符替换成下划线避免跑一半中断。另外重命名前最好先打印一份“原文件名→新文件名”的对照表确认无误再真正执行尤其是批量处理重要文件时这个预览动作能救你一次。2.3 PDF合并与水印脚本三PDF 合并是我给朋友处理最多的一类需求。比如把纸质合同扫描成多页单页 PDF需要合成一份完整文档或者把若干份电子发票合并成一个 PDF 文件方便归档报销。现在我已经不用手动在工具软件里一张张贴了直接用pypdf写一个合并脚本from pypdf import PdfWriter, PdfReader from pathlib import Path pdf_dir Path(D:/PDF资料) output PdfWriter() for pdf_path in sorted(pdf_dir.glob(*.pdf)): reader PdfReader(pdf_path) for page in reader.pages: output.add_page(page) print(f已加入{pdf_path.name}) output.write(合并结果.pdf) print(合并完成)这个脚本把目录下所有 PDF 按文件名排序后逐页追加最后输出一份新的合并文件。实际过程中我有两个额外需求其一有些 PDF 扫描件方向不对需要自动旋转其二需要在每页里加上“仅用于归档”之类的水印。pypdf里的page.rotate(90)可以直接旋转页面。水印则稍微绕一点你需要先用一个单独的 PDF 作为水印模板再把每一页合并上去。这里我建议你把水印内容用独立工具生成好脚本只负责“叠印”这样维护水印内容时不用改 Python 代码。处理大量 PDF 时还有一个容易被忽略的点如果某些 PDF 带有密码保护读取时直接报错代码里要捕获pypdf.errors.FileNotDecryptedError并提示用户单独处理。我的做法是在遍历时先 t 5 次解密解密失败就跳过并把文件名记录到日志里。3. 网页与数据采集让脚本替你“蹲守”消息3.1 商品价格监控提醒脚本四不知道你有没有过这种经历心仪的商品加入购物车后每天打开网页看价格看了半个月也没见降价某天终于降了却因为没及时下单又错过了。我以前想买个显示器就是这种状态。后来写了一个价格监控脚本让它每隔几小时帮我查一次价格低于预期就让脚本发邮件通知我。这里的核心是两个动作先从网页里解析出价格再跟目标价比较符合条件就触发通知。import os import requests from bs4 import BeautifulSoup import smtplib from email.message import EmailMessage url 商品页面URL target_price 1999.0 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } resp requests.get(url, headersheaders, timeout10) resp.encoding utf-8 soup BeautifulSoup(resp.text, html.parser) price_node soup.select_one(.current-price) price_text price_node.text.strip().replace(, ).replace(,, ) price float(price_text) if price target_price: sender senderexample.com receiver receiverexample.com password os.environ.get(SMTP_PASS) msg EmailMessage() msg[Subject] 价格监控提醒 msg[From] sender msg[To] receiver msg.set_content(f当前价格 {price} 元已达到你的预期。) with smtplib.SMTP_SSL(smtp.example.com, 465) as smtp: smtp.login(sender, password) smtp.send_message(msg) print(已发送提醒邮件) else: print(f价格未达预期当前 {price} 元)网页解析这一步核心是找到价格所在的 HTML 元素。每个网站结构不一样你需要先用浏览器开发者工具查看元素再用select_one定位。如果只依赖某个特定的 CSS 类名网站一旦改版就可能失效所以我的建议是把选择器单独抽成一个配置项方便随时改而不用动主逻辑。爬取别人网站时要有基本的礼貌。我给自己定的规矩是请求间隔至少 30 分钟并且只在页面公开可访问的前提下做监控。这样既满足个人需求也不会给目标服务器造成压力。3.2 每日天气播报脚本五给家里老人或者工作群自动推送天气是个既贴心又能省事的脚本。我最早是因为每天早晨都要手动查天气再转发到家庭群持续了两个月实在受不了于是让脚本代劳。思路不复杂选择一个免费天气 API注册后拿到一个 key然后请求指定城市的天气数据再通过企业微信群机器人或者短信平台推出去。下面这个示例用到了最通用的流程import os import requests city 某城市 api_key os.environ.get(WEATHER_API_KEY) url fhttps://api.example.com/weather?city{city}key{api_key} resp requests.get(url, timeout5) data resp.json() temperature data[current][temperature] weather_desc data[current][description] tip 出门记得带伞 if 雨 in weather_desc else 天气不错适合出行 message f{city}今日天气{weather_desc}气温 {temperature}°C。{tip} print(message)天气 API 的选择有几个注意点注册后要看清免费额度比如每天请求次数上限和付费规则返回的 JSON 结构各平台不同不要照着我的代码硬套先打印一次data看看实际字段名。我自己的习惯是把这个脚本挂在系统定时任务里每天早晨七点半跑一次让推送消息自动出现在群聊里。定时任务怎么挂我在后面的章节统一说。3.3 网页正文提取把长文变成本地笔记脚本六有时候你刷到一篇好文章想收藏到本地做笔记但网页排版复杂复制粘贴又容易把广告和无关内容一起带过来。我在学习技术资料的时候经常遇到这件事后来写了这个“网页正文提取”脚本只需要给一个 URL就能把标题和正文保存成 txt 文件。import requests from bs4 import BeautifulSoup url 文章链接 resp requests.get(url, timeout10) resp.encoding resp.apparent_encoding soup BeautifulSoup(resp.text, html.parser) title soup.title.get_text(stripTrue) body soup.select_one(article) if not body: body soup.body lines [line.strip() for line in body.get_text(\n, stripTrue).splitlines() if line.strip()] content \n.join(lines) with open(f{title[:30]}.txt, w, encodingutf-8) as f: f.write(content) print(f已保存文章{title})这段代码最关键的坑在编码。很多网页不是 UTF-8requests默认猜的编码不一定对中文很容易乱码。我一般先设置resp.encoding resp.apparent_encoding让requests自己从 HTML 的 meta 信息里去猜比写死一个编码更可靠。另外不同的网站正文结构不同有的用article有的用div classcontent。第一次使用某个网站时最好先用开发者工具看一眼正文所在节点然后把选择器配置到脚本开头而不是每次临时改代码。我保留过一个几十行的配置文件把常见网站的选择器都收集起来更新很方便。4. 办公与沟通自动化让脚本替你发消息4.1 多个Excel汇总成一张报表脚本七做运营和财务的朋友应该很有共鸣月底要从十几个门店发来的 Excel 里汇总一张总表格式还不完全一样有的带标题行有的不带。我以前处理这种表格直接使用最笨的复制粘贴后来用pandas一行代码就把活接住了。import pandas as pd from pathlib import Path folder Path(D:/报表) frames [] for file in folder.glob(*.xlsx): df pd.read_excel(file) print(f导入{file.name}形状{df.shape}) frames.append(df) merged pd.concat(frames, ignore_indexTrue) merged.to_excel(汇总报表.xlsx, indexFalse) print(f合并完成共 {len(merged)} 行)这个脚本帮我把“几十个文件逐一打开、复制、粘贴”的流程压缩成了“双击运行、等两秒、完成”。不过用pandas读 Excel 有几个隐藏细节。第一文件后缀是.xls老格式时需要额外安装xlrd第二如果每个分表的第一行不是真正的表头要加header参数从指定行开始读取第三如果列名在不同表中不完全一致pd.concat会按列名自动对齐缺失部分会补 NaN你后续要决定是删掉还是填充。我认为最好的实践是先让所有分表使用同一套标准模板源头一致汇总时才不会产生各种奇奇怪怪的脏数据。4.2 定时发送邮件报告脚本八每周五下午给团队发工作周报是我坚持了好几年的习惯。以前都是手动写邮件、查附件、点发送直到有一次忘了发被追问了才发现。后来我把这套流程脚本化每周自动运行稳定得让人放心。SMTP 发邮件的原理不复杂通过你所用邮箱提供的 SMTP 服务器用账户密码登录再构造一封邮件发送出去。Python 标准库里的smtplib和email就够用了。import os import smtplib from email.message import EmailMessage sender senderexample.com receiver receiverexample.com password os.environ.get(SMTP_PASS) msg EmailMessage() msg[Subject] 每周工作汇报 msg[From] sender msg[To] receiver msg.set_content(这是本周的自动汇报邮件请查收附件。) with open(周报.xlsx, rb) as f: msg.add_attachment(f.read(), maintypeapplication, subtypevnd.openxmlformats-officedocument.spreadsheetml.sheet, filename周报.xlsx) with smtplib.SMTP_SSL(smtp.example.com, 465) as smtp: smtp.login(sender, password) smtp.send_message(msg) print(邮件已发送)有两个经验必须分享。第一密码不要硬编码在代码里我通常把它放在环境变量中脚本里用os.environ.get(SMTP_PASS)读取。如果别人拿到你的脚本文件也不至于直接看到邮箱密码。第二用免费邮箱时不要直接写邮箱本身的主密码而是去账户设置里开通 SMTP 服务生成独立授权码用授权码登录。如果你要发送的邮件频率很高建议在代码里加一个异常捕获发送失败时把错误信息写入日志再配合系统任务的重跑机制比每次都盯着屏幕更可靠。4.3 合同批量生成脚本九需要批量生成几十份内容相似、只有少数字段不同的合同时手动复制粘贴替换文字往往要花费很长时间还容易漏改。我借助docxtpl用 Word 模板配合一份数据表就能批量生成格式统一的文档。首先你在 Word 模板里用{{ 字段名 }}的形式挖好坑比如甲方{{ party_a }} 乙方{{ party_b }} 项目名称{{ project_name }} 签订日期{{ sign_date }}然后在 Python 中准备一个字典字段名对应模板里的占位符执行渲染后保存成新文件from docxtpl import DocxTemplate template_path 合同模板.docx data { party_a: 某公司, party_b: 某乙方代表, project_name: 模拟项目X, sign_date: 2025-01-15, } tpl DocxTemplate(template_path) tpl.render(data) tpl.save(合同_某乙方代表.docx) print(已生成合同文件)大规模批量生成时我一般会用一个文本文件存放所有合同的数据每行一条记录然后循环读取模板并渲染。比如同时生成 50 份实习协议每份都以员工姓名命名整个过程不到一分钟。需要注意docxtpl使用的是 Jinja2 语法如果模板里某个字段循环取值也可以写{% for item in items %}功能比单纯替换占位符更灵活。只是模板文件里的语法一旦写错渲染时会直接报错所以我会先拿一条数据做试渲染验证样式和内容无误后再批量跑。另外生成的文档建议存放在独立目录避免和模板源文件混在一起。5. 系统维护与任务调度把电脑当成你的员工5.1 磁盘空间巡检与过期临时文件清理脚本十电脑磁盘空间越来越小是很多人没意识到的隐患。我之前有台工作机C 盘常年标红直到某次编译大项目时直接报磁盘不足才被迫开始清理。排查完发现大部分空间都被临时文件和缓存占着。这个巡检脚本会扫描几个常见的临时目录找出超过指定天数没有改动的文件或文件夹然后送到回收站而不是永久删除。from pathlib import Path import time import send2trash CACHE_DIRS [ Path.home() / .cache, Path.home() / AppData/Local/Temp, ] DAYS 7 SECONDS DAYS * 24 * 3600 for folder in CACHE_DIRS: if not folder.exists(): continue for item in folder.iterdir(): try: if item.stat().st_mtime time.time() - SECONDS: send2trash.send2trash(str(item)) print(f已移入回收站{item}) except PermissionError: continue这个脚本有两点我要重点强调。一是路径路径不同系统缓存目录差异很大macOS 的缓存目录和 Windows 完全不同我上面的示例是 Windows 风格的路径macOS 用户需要改成~/Library/Caches等目录。二是权限部分临时文件正在被系统或程序占用直接删除会失败代码里要捕获异常跳过这些文件不能因为单个文件报错就中断整个流程。使用send2trash而不是shutil.rmtree是我坚持的安全底线。文件先进回收站万一误删还有后悔药可吃。跑完脚本后我都会看一眼回收站确认没有误伤正常文件再清空。5.2 把这10个脚本统一交给定时调度器单个脚本写完还只是第一步。真正让它们发挥价值的是定时运行。我在本地用的是 Python 的schedule库把所有任务统一放到一个调度文件里后台持续运行import schedule import time schedule.every().day.at(09:30).do(organize_downloads) schedule.every().day.at(07:30).do(send_weather) schedule.every().monday.at(10:00).do(send_report) while True: schedule.run_pending() time.sleep(60)schedule库最大的优点就是简单几乎没学习成本。但它只能在你打开这个 Python 进程的时候运行如果你退出终端、重启电脑它就停了。所以生产环境或者说“长期可靠”的环境我建议把脚本注册到系统任务计划程序里面比如 Windows 的“任务计划程序”或者 macOS/Linux 的cron。我自己是双重方案开发期用schedule做快速测试稳定之后改用系统任务调度。不管用哪种方式都要在计划里指定完整的 Python 解释器路径和脚本绝对路径。只写“python xxx.py”这种相对路径在计划任务里经常因为当前工作目录不同而找不到文件。5.3 安全底线自动脚本必须设好“刹车”自动化脚本最大的风险不是写不出来而是“跑起来之后不受控”。我在迭代过程中给自己立了几条规矩也建议把它们写进你的脚本习惯里。第一删除类操作默认走回收站绝不直接rm -rf。第二批量修改文件之前先做一次试运行只打印将要执行的操作名称不真正动手。第三脚本中涉及的重要操作都写日志保留至少最近 30 天的记录方便排查。第四对文件做移动、重命名、覆盖前先判断目标是否存在存在时优先重命名而不是直接覆盖。这些规则能让你在脚本出错时不至于手忙脚乱也能避免因为一个小失误造成大范围文件丢失。6. 常见问题与排查技巧实录6.1 脚本运行没反应先检查这三处遇到脚本明明执行了却没达到预期效果我一般按顺序排查三个点。首先是“用哪个 Python 环境”如果你在终端里输入python进入的环境不是安装依赖的那个虚拟环境脚本可能因为缺第三方库直接报错。其次是“当前目录是否正确”脚本如果用了相对路径比如Path(D:/待整理文件)那么执行时工作目录不在预设位置路径就会失效。第三是“异常有没有被吞掉”很多脚本只写了业务逻辑没做错误处理一旦某一步抛异常整个流程就静默退出你根本不知道发生了什么。我建议在每个脚本的入口加上一段异常兜底import logging logging.basicConfig(filenameauto_script.log, levellogging.INFO, format%(asctime)s %(levelname)s %(message)s) try: main() except Exception as e: logging.exception(脚本执行失败)这样至少失败时能看到日志而不是干瞪眼。6.2 中文乱码问题Python 脚本处理中文时常见的乱码有三种来源源码文件本身的编码、读取外部文件时的编码、输出到终端时的编码。我的统一做法是所有.py文件用 UTF-8 保存读取外部文本文件时指定encodingutf-8Windows 终端如果显示乱码执行前在脚本开头加sys.stdout.reconfigure(encodingutf-8)或者chcp 65001切换代码页。处理网页数据时我前面提过用resp.encoding resp.apparent_encoding来猜编码这个方法比硬编码utf-8更能适配不同站点。处理 Excel 时pandas读出来的中文一般不会乱但如果你写 CSV 而不是 Excel记得用encodingutf-8-sig这样文件用 Excel 打开时就不会出现乱码。6.3 路径跨平台问题初学者最容易踩的坑是在代码里硬编码路径分隔符比如D:\\数据\\xxx。这种写法写死了 Windows 风格迁移到 macOS 就崩。我从一开始就改用pathlib.Path路径拼接用/运算符例如Path.home() / Downloads。它会在各个平台自动转换为正确的分隔符而且代码读起来也更清晰。另外脚本里尽量不要依赖“当前工作目录”。我现在的规范是每个脚本开头都定义好基础路径常量用绝对路径或基于Path.home()的路径。这样不管是手动执行还是定时任务执行都不会因为工作目录不同而出错。6.4 给脚本加日志和异常兜底等于买了保险脚本刚开始运行时可能只有你自己盯着终端看。但当它挂到定时任务里后你很少主动盯输出这时候日志就变成了唯一的诊断依据。我推荐在脚本里统一初始化logging日志文件按天滚动避免越积越大。import logging from logging.handlers import TimedRotatingFileHandler handler TimedRotatingFileHandler( auto_script.log, whenmidnight, backupCount30 ) logging.basicConfig( levellogging.INFO, format%(asctime)s [%(levelname)s] %(message)s, handlers[handler], )这样做的好处是脚本没跑可以先看日志确认时间对不上跑失败了看最后一条日志能定位到具体是哪一步异常。我自己调试价格监控脚本的时候就是靠日志发现请求被目标网站限制后来才决定把请求频率调到更保守。7. 写在最后自动化最大的阻力不是技术是习惯说了这么多脚本最后讲一点个人体会。很多人学自动化脚本的时候总想着做一套“大而全”的东西结果写了一半就放弃了。我第一版文件分类器写得很烂代码只有几十行但已经够用后来才逐步加规则、加重名保护、加日志。如果你也想用 Python 解放双手我的建议很简单别想着一次性自动化所有事情先从你下周已经确定会重复做的那件事开始。可以是整理一次下载文件夹也可以是生成一次周报邮件。等你尝到“脚本代替手动”的甜头之后自然会上瘾后面每遇到一件重复劳动你都会条件反射地思考“这能不能脚本化”。我就是这样从一个手动整理文件都要花一小时的人变成了现在每天早晨看一眼日志就放心开始工作的人。自动化的价值不在于一次省了多少时间而在于你终于不用再为那些不值得占用脑力的事情消耗精力了。最后再送一个小技巧给所有脚本统一加上--dry-run参数让它只打印将来要做什么而不真正执行。我因为加了这一步至少避免了两三次“批量误操作”的惨案。写代码慢一点没关系放心运行才更重要。