1. 为什么我开始写自动化脚本:一个周末的顿悟
我至今还记得那个周六的下午。桌面上堆着几十个乱七八糟的文件——合同扫描件、产品截图、各种版本的PPT、从客户那边拷过来的PDF,命名从"新建文档(7).docx"到"最终版最终决定不再改v3.pptx"应有尽有。我花了整整四十分钟,一个个把它们拖进对应的文件夹,改好名字,然后看着那堆仍然混乱的下载目录,心里冒出一个念头:这种事,我已经干过多少次了?
那天我做了个决定:与其继续手动整理,不如写一个Python脚本来干这件事。于是就有了这篇文章——一个完全从日常琐事出发、不涉及任何高深算法、纯粹为解决真实麻烦而诞生的脚本。它不是什么完美作品,但它的确让我从每周至少半小时的文件整理劳动中彻底解放出来。
我想写这篇文章的原因很简单:很多人一提到"写脚本"就觉得是程序员的事,或者觉得需要掌握什么高深的编程知识。但实际上,脚本的本质就是"把你在电脑上反复做的那些机械操作,写下来让电脑替你执行"。它不需要你成为专家,只需要你把需求拆清楚,然后照着最朴实的方式来就行。
这篇文章适合这么几类人:被重复性文件操作折磨的办公族、想学Python但不知道从哪下手的入门者、以及那些已经会写两行代码但始终没把"自动化思维"应用到日常工作中的人。我会完整走一遍脚本从需求分析、环境搭建、编码实现到反复修正的全过程,把我踩过的坑和当时的思考都摊开来讲。
2. 脚本诞生第一步:拆解需求,把事情说清楚
2.1 先别急着写代码:把你的"手工流程"完整记下来
我见过太多人学习编程时犯同一个错误:拿到需求就上手写代码,结果写到一半发现逻辑漏洞百出,再回头改,改得自己都糊涂了。写自动化脚本尤其忌讳这一点——因为脚本的本质是"流程的机械复制",你连流程本身都没弄清楚,复制什么呢?
我处理文件整理这个需求时,第一步不是打开编辑器,而是坐下来把自己每次整理文件的动作一条条写下来。这个动作非常关键,我强烈建议你也这么做。
我当时写下的是这样的流程:
- 查看下载目录里所有文件的类型
- 根据扩展名判断文件属于什么类别(图片?文档?压缩包?)
- 找到或创建对应的分类文件夹
- 把文件移动过去
- 顺手把一些明显是"临时文件"的东西删掉
看起来很简单对吧?但当你真正写下来,就会发现里面藏着不少"潜规则"——比如"根据扩展名判断类别"这件事,哪些扩展名归图片?哪些归文档?那些没见过的扩展名又该怎么处理?这些规则不明确下来,写出来的脚本就只能是一团浆糊。
我把这些规则在纸上(真的是纸上,我找了一张 A4 纸)画出了一个简单的分类表:
| 扩展名类别 | 包含的具体扩展名 | 目标文件夹 |
|---|---|---|
| 图片 | .jpg, .jpeg, .png, .gif, .bmp, .svg | 下载目录/图片 |
| 文档 | .pdf, .doc, .docx, .ppt, .pptx, .xls, .xlsx, .txt | 下载目录/文档 |
| 压缩包 | .zip, .rar, .7z, .tar, .gz | 下载目录/压缩包 |
| 视频 | .mp4, .avi, .mkv, .mov, .flv | 下载目录/视频 |
| 音频 | .mp3, .wav, .flac, .aac | 下载目录/音频 |
| 程序安装包 | .exe, .msi, .dmg | 下载目录/安装程序 |
这个表格看起来平平无奇,但它就是整个脚本的"灵魂"。因为代码本身只是在执行这张表上的规则——用什么扩展名、移到哪、跳过哪些文件,这些决策我在写代码之前就已经全部做完,写代码反而成了最简单的一个环节。
2.2 边界情况:脚本最怕的不是复杂,是"没想到"
需求拆解过程中,最考验人的不是主流程设计,而是边界情况的处理。说白了就是那些"平时手工处理时顺手就做了、但写规则时才发现很麻烦"的情况。
我列了一堆边界问题,现在挑几个有代表性的分享出来:
第一个是"未分类文件怎么办"。下载目录里总有一些不知道是干嘛的文件——像什么 .tmp、.crdownload,或者某个装完软件后留下的 .log。手动整理时我看一眼就知道是干什么的,但脚本没有这个"看一眼"的能力。我的方案是单独建一个"其他"文件夹,把这些不确定的文件丢进去,保留进一步筛选的可能性。
第二个问题是"文件名重复"。以前手动整理的时候,如果目标文件夹里已有同名文件,我会自己改个名或者稍微变一下。但脚本怎么处理?覆盖显然有风险,不处理又会报错。我在需求清单上写下了自己的策略:如果遇到重名文件,自动在命名后面加上时间戳,比如"报告_20240515_143022.pdf",保留所有文件不丢失。
第三个问题更隐蔽——"这根本不是文件整理,而是两个问题的混合"。下载目录里除了待分类的文件,还有那种下了一半的临时文件。它们的存在会导致脚本误判,也可能在分类后污染目录。所以我额外加了一条规则:.crdownload 和 .tmp 后缀的文件直接删除。
第四个问题是"什么是最近的文件"。我最初的需求不仅是整理文件,还想让脚本"清理掉长期不用的老文件"——但什么叫"长期不用"?以什么时间为基准?行业标准做法是以文件修改时间或者最后访问时间(如果是脚本读取的话)来判断。我在需求里定义了一个阈值参数:超过180天未修改且不在指定保留列表里的文件,归入"待清理"文件夹,而不是直接删除——先软后硬,误删了还能救回来。
2.3 建立脚本骨架:伪代码比代码本尊更重要
等规则想清楚,我没有急着写真正的Python代码,而是先用"伪代码"——一种用自然语言模仿代码结构的方式——把整个逻辑串了一遍。这一步帮我在真正动手前就发现了好几处逻辑不顺的地方。
那个伪代码草稿大致长这样:
对于下载目录中的每个文件: 获取文件后缀名 如果后缀名在图片列表里: 移动到 下载目录/图片 如果后缀名在文档列表里: 移动到 下载目录/文档 如果后缀名是临时文件类型: 删除 如果后缀名不在任何列表里: 移动到 下载目录/其他 如果目标位置已有同名文件: 构造带时间戳的新文件名这个骨架后来被我在实际写代码时基本原样照搬。它的价值在于:它把"需求"翻译成了"逻辑",又把"逻辑"翻译成了"结构",让我在写第一行代码之前就知道整个脚本的形态。对这个脚本来说,逻辑就这么几条;但对更复杂的自动化任务来说,这一步可以帮你发现哪些环节是并行的、哪些环节有先后依赖,从而设计出更合理的函数结构。
3. 从零搭建环境:别在第一步就劝退自己
3.1 安装Python的版本选择与验证
写Python脚本,第一步当然是准备环境。但很多人恰恰是卡在这一步——去官网下哪个版本?装完怎么验证?为什么我的电脑命令行里输入python不是期望的结果?这些问题看起来低级,却是新手放弃率最高的环节。
我当时的电脑是一台很普通的 Windows 笔记本。根据Python官方现在的情况,我建议直接下载 3.10 以上版本,比如当前的稳定版 3.12.x——不需要纠结什么"会不会太新不稳定"。这个领域的原则很简单:脚本是给自己用的,版本选新的,文档多,遇到问题搜到的答案也多,比选一个保守的旧版要舒服得多。
下载安装包时,特别注意一个复选框:Add python.exe to PATH。很多人忽略它,接着就会遇到"我明明安装了Python,但命令行里输入python却提示不是内部或外部命令"这种尴尬。勾上这个选项,相当于告诉Windows"以后我输入python时,你知道该去哪找这个命令"。如果忘了勾,强烈建议重装一次,或者手工去系统环境变量里手动把Python路径加上——相比之下重装反而省事。
装完之后怎么验证?打开cmd或者PowerShell,输入这个命令:
python --version如果看到类似Python 3.12.4的输出,说明环境没问题。我再建议顺手验证一下pip——Python的包管理器,后面安装依赖库就靠它:
pip --version这两个命令都正常,环境就算跑通了。回到我们的场景,这段装环境的经历其实占不了十分钟,但它决定你整个脚本能不能顺利运行,所以值得认真对待。
3.2 VS Code还是纯记事本?我的选择
关于写代码用的工具,新手容易陷入"哪个最好用"的争论。我的建议很简单:选VS Code,够用且好用,关键是插件生态成熟。你可以用它写Python脚本、调试代码、管理工程,后面玩爬虫、写数据分析,它都能撑住。
VS Code装好后,需要装Python插件。过程很快,在扩展市场里搜索"Python",选择那个微软官方发布的(作者显示为Microsoft),安装,搞定。这个插件会给你代码补全、语法检查、一键运行等功能。
不过有一件事我要特别重点说:VS Code里运行Python脚本的前提是你已经正确安装了Python,并且VS Code能识别到你的解释器。新手经常在这里懵掉。我的做法是装完插件后,打开命令面板(Ctrl+Shift+P),输入"Python: Select Interpreter",选择一个跟当前Python环境匹配的选项。如果选择正确,左下角状态栏会显示当前的Python版本号。
如果你觉得VS Code实在折腾,也可以先直接在记事本里写.py文件,用命令行运行。我早期就是这么干的——脚本规模不超过一屏的话,完全可行。但一旦逻辑复杂起来要调试,VS Code的优势就非常明显了。
3.3 我的目录规划设计
环境就绪后,我在本地建了一个专属的自动化项目目录。这不是仪式感,而是实操中非常必要的一步:你写的脚本会逐渐增多,如果每个脚本都随意丢一个位置,后面找起来就是灾难。
我的做法是:
E:\AutoScripts\ ├── file_organizer\ │ ├── organize_downloads.py │ └── README.md └── other_scripts\在每个子目录里,我还习惯放一个简短的README.md,记录这个脚本的用途、依赖的库、怎么调用。这在初始阶段看似多余,但三个月后当你回头找"那个整理文件的脚本"时,你就知道这东西有多值钱。
4. 第一版脚本的完整实现:从伪代码到能跑
4.1 核心代码逐段拆解:每行都有它的理由
当我准备写正式代码时,我把伪代码翻译成了Python,分成了几个清晰的模块。以下是我最终写的核心代码片段,我会逐段解释,不仅是"它在干什么",还要说明"为什么这么写"。
首先是导入库和定义配置:
import os import shutil from datetime import datetime from pathlib import Path # 目标目录:可以改成任何你实际想整理的目录 DOWNLOAD_DIR = Path.home() / "Downloads" # 分类规则:扩展名映射到目标文件夹 CATEGORY_RULES = { "图片": [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".svg"], "文档": [".pdf", ".doc", ".docx", ".ppt", ".pptx", ".xls", ".xlsx", ".txt"], "压缩包": [".zip", ".rar", ".7z", ".tar", ".gz"], "视频": [".mp4", ".avi", ".mkv", ".mov", ".flv"], "音频": [".mp3", ".wav", ".flac", ".aac"], "安装程序": [".exe", ".msi", ".dmg"], } # 临时文件后缀,直接清理 TEMP_SUFFIXES = [".tmp", ".crdownload", ".part"] # 待清理目录(超过180天未修改的文件会移到这里) QUARANTINE_DIR = DOWNLOAD_DIR / "待清理" CLEAN_THRESHOLD_DAYS = 180这里我把规则集中放在脚本开头。很多人喜欢把规则散落在代码各处,后面要修改时得翻半天。集中常量定义的好处是,未来你只需要改这一块,脚本主体逻辑完全不用动。比如哪天你收到了.webp格式的图片,把它加到"图片"列表里就行。
然后是核心的文件分类函数:
def categorize_file(suffix: str) -> str: """根据文件后缀名返回所属类别,未匹配则返回'其他'""" for category, suffixes in CATEGORY_RULES.items(): if suffix.lower() in suffixes: return category return "其他"这个函数用的是枚举匹配逻辑,效率虽然不算最优(如果规则很多,可以用字典反转),但胜在直观,一眼就能看懂。需要注意我调用了.lower()——因为文件后缀可能是.JPG也可能是.jpg,统一转小写再匹配,就避免了大小写导致的漏分问题。
接着是移动文件时的重名处理:
def unique_destination(dest_dir: Path, filename: str) -> Path: """如果目标路径已存在同名文件,生成带时间戳的新文件名""" candidate = dest_dir / filename if not candidate.exists(): return candidate stem = Path(filename).stem suffix = Path(filename).suffix timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") return dest_dir / f"{stem}_{timestamp}{suffix}"这里我考虑的是,现实中同名文件概率其实不高,但一旦撞上,如果没有处理逻辑,shutil.move()可能会直接覆盖同名文件,造成数据丢失。在自动化脚本中,"不丢数据"是最高原则——New 用户可能没意识到,但批量操作中的覆盖是数据丢失最常见的元凶。加一个时间戳,损失一点文件命名的整洁度,换来绝对不会覆盖的安全感,这笔账划算。
然后是核心的"整理一个文件"的函数:
def organize_file(file_path: Path): """处理单个文件:判断类型、创建目标目录、移动文件""" if not file_path.is_file(): return suffix = file_path.suffix # 临时文件直接删除 if suffix.lower() in TEMP_SUFFIXES: print(f"[删除临时文件] {file_path.name}") file_path.unlink() return # 判断类别 category = categorize_file(suffix) dest_dir = DOWNLOAD_DIR / category # 如果文件已经在其所属分类目录里,跳过 if file_path.parent == dest_dir: return # 创建目标目录(如果不存在) dest_dir.mkdir(exist_ok=True) target_path = unique_destination(dest_dir, file_path.name) print(f"[移动] {file_path.name} -> {category} / {target_path.name}") shutil.move(str(file_path), str(target_path))这段代码的每个步骤都有对应需求里的那条规则。特别提一下file_path.parent == dest_dir这个判断——这是我修了两次才加上的。最初没有这个判断时,脚本会把已经整理好的文件再"整理"一遍,虽然结果没问题,但会多出一堆无意义的日志输出,也浪费时间。这个问题的本质是:你在整理一个目录时,需要排除"目标目录本身就是源目录的子目录"这种情况,否则脚本会在自己创建的文件夹里无休止地扫下去。
接下来是清理逻辑:
def clean_old_files(directory: Path, threshold_days: int): """把超过threshold_days未修改的文件移入待清理目录""" quarantine_dir = QUARANTINE_DIR quarantine_dir.mkdir(exist_ok=True) now = datetime.now() for file_path in directory.iterdir(): if not file_path.is_file(): continue mtime = datetime.fromtimestamp(file_path.stat().st_mtime) age_days = (now - mtime).days if age_days > threshold_days: print(f"[清理] {file_path.name} (已 {age_days} 天未修改)") target_path = unique_destination(quarantine_dir, file_path.name) shutil.move(str(file_path), str(target_path))这里我用了st_mtime(最后修改时间)而不是st_atime(最后访问时间)。原因是Windows系统下"最后访问时间"的读取策略有时会disable或被系统更新任务干扰,不够可靠;而修改时间基本是稳定的。你可能觉得宏观上差别不大,但实际运行中,某些系统优化软件会批量刷新访问时间,导致误判。
最后是主函数入口:
def main(): print("开始整理下载目录...") for file_path in DOWNLOAD_DIR.iterdir(): organize_file(file_path) clean_old_files(DOWNLOAD_DIR, CLEAN_THRESHOLD_DAYS) print("工作完成。") if __name__ == "__main__": main()if __name__ == "__main__":这行代码是Python约定俗成的入口写法。它的作用是:当这个脚本被直接运行时,执行main();当它被作为模块导入到其他脚本时,不自动执行。我刚开始写脚本时不习惯写这行,后来发现要给脚本写单元测试或者被别人调用时,没有这行会非常不方便。
4.2 运行脚本,第一次见到报错时的正确处理方式
把代码写完、文件保存为organize_downloads.py后,到了验证的时刻。在VS Code里我直接右键选择"Run Python File in Terminal",第一次运行的结果自然不是一帆风顺——但我大概只花了15分钟就把问题解决了,这个过程本身就很值得分享。
第一次运行遇到的错误是:
PermissionError: [Errno 13] Permission denied: 'E:\\Downloads\\xxx.dll'我的第一反应不是慌,而是读错误信息。PermissionError的含义是权限问题——脚本没有权限移动这个文件。我当时想了下,有几个可能的原因:文件被Python自身进程锁住了(不太可能)、文件被系统或后台进程占用(更常见)、或者这个文件是只读属性(罕见)。
最后我排查到原因:某个.dll文件正被Windows资源管理器预览进程占用。解决办法也简单——在脚本里,当目标文件被别的进程占用时,捕获这个异常并且跳过,而不是让整个脚本崩溃退出。
于是我在organize_file里加了 try-except 包裹:
try: shutil.move(str(file_path), str(target_path)) except PermissionError: print(f"[跳过-权限错误] {file_path.name},文件可能正被占用") except Exception as e: print(f"[跳过-未知错误] {file_path.name}: {e}")这个改动看起来很小,但价值很大——脚本从"遇到一个错误就崩溃"变成了"遇到一个文件出问题就跳过,继续处理剩下的",这中间的可靠性和实用性差了一个量级。在自动化脚本的世界里,"部分成功"远比"整体失败"真实和有用。
第二次运行,所有文件顺利归位。看到下载目录瞬间清空的输出日志时,那种成就感是实实在在的。
4.3 用命令行参数让脚本更灵活
脚本跑通之后,我很快就遇到了新需求:我想把Downloads目录整理的逻辑,同时也用于桌面或者某个临时项目目录。总不能为每个目录复制一份脚本吧?这就是命令行参数的作用。
我用Python标准库里的argparse给它加了参数支持:
import argparse def parse_args(): parser = argparse.ArgumentParser(description="文件整理脚本") parser.add_argument("--target-dir", type=str, default=str(DOWNLOAD_DIR), help="要整理的目录路径") return parser.parse_args()然后在main()里接收参数,替换掉写死的DOWNLOAD_DIR。改完之后,我就可以这样用了:
python organize_downloads.py --target-dir "E:\临时文件" python organize_downloads.py --target-dir "C:\Users\me\Desktop"这一步让脚本的适用面一下子打开了。我后来的经验是:一个自动化脚本,如果只是针对固定场景的硬编码版本,那它只是"工具";一旦加上参数化配置,它就变成了可以复用的"能力"。这个区别在自动化这条路上很重要。
5. 从"能用"到"好用":那些运行时才暴露的问题
5.1 日志输出:别让脚本变成黑盒
初版脚本运行时,控制台会打印类似这样的信息:
[移动] 项目草案.pdf -> 文档 / 项目草案.pdf [删除临时文件] cache.tmp [移动] holiday_photo.JPG -> 图片 / holiday_photo.JPG这些输出看起来简单,但它们是整个脚本唯一的"可观测性"。在真实使用中我才发现,一个完全没有输出的脚本会让你完全抓瞎——它到底跑了没有?跑完了哪些?哪些文件被处理了?出错了没?出了几个错?你什么都不知道。
所以我后续给脚本加了个简单的"日志文件"功能,把运行记录同时写入一个日志文本。这不是什么高级操作,只是把原来print的内容同步写进文件:
import logging logging.basicConfig( filename="organizer.log", level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" )之后把原来的print换成logging.info即可。这个改动的收益是巨大的:当某天你发现有个文件被意外移走但找不到来源时,翻日志一目了然。自动化脚本里的日志不是给机器看的,是给未来某个时刻的自己看的。
5.2 安全阀:自动操作必须有"后悔药"设计
自动化脚本最让人担心的是什么?是误操作之后没有挽回余地。我见过有人用自动脚本清理垃圾文件,结果正则写错了,把重要备份直接删了——这种事发生一次就足以让你对自动化产生心理阴影。
所以我给这个脚本设计了一个安全机制:默认启用"预览模式"。在预览模式下,脚本只打印将要执行的操作,并不真正移动或删除文件:
PREVIEW_MODE = True def organize_file(file_path: Path, preview: bool = True): ... if preview: print(f"[预览] 将要移动 {file_path.name} -> {category}") return ...预览模式让脚本变得"可审查"——运行一次预览,确认输出符合预期后,再实际上线。这个开关被我保留在脚本里,后来演变成了--preview参数。这算是自动化脚本领域的一条黄金法则:任何批量修改文件的脚本,都先设计成无害的"干跑",再允许真实执行。
5.3 定时自动运行:让脚本自己"醒过来"
脚本能手动运行,但离"自动化日常工作"还差最后一环——定时触发。Windows上有简单可靠的方式,我先把两种方案说明白。
第一种是使用Windows的任务计划程序。在Windows搜索栏输入"任务计划程序",打开后创建一个基本任务,触发器选"每天"或"每周",操作选"启动程序",程序填python,添加参数填脚本路径。就这么简单。
第二个方案是用Python自己写个死循环加时间判断。很多人喜欢这种写法,但我不推荐长期用——它需要始终保持一个Python进程占用内存,而且崩溃了没有任何恢复机制。用操作系统的计划任务,任务的可靠性和资源占用都比自己写的循环好很多。
不过,在设定自动运行之前,有一点必须注意:你的脚本必须具备"可重复运行"的能力。什么叫可重复运行?就是同一个脚本运行第二次、第三次,结果仍然是正确的,不会因为第一次运行产生了新的目录结构而报错。这要求脚本里所有mkdir操作都带上exist_ok=True,所有移动逻辑都不依赖"目标目录初始为空"这个假设。我给脚本加上这些调整后,才安心设置定时任务。
5.4 桌面双击启动:给不熟悉命令行的人用脚本
脚本跑起来是命令行输出,但如果我要把这个"整理工具"给家里人用,总不能让他们去打开终端、输入命令吧?对普通用户,最友好的界面就是双击桌面图标。
我的方案是写一个简单的.bat文件放在桌面上,内容只有一行:
@echo off python E:\AutoScripts\file_organizer\organize_downloads.py pause这样一个双击就能运行脚本,窗口还停留在那里显示结果,不会一闪而过。对于技术能力有限的用户,这样就是"能用"的标准,不用再教他们什么是命令行。
后来我还想再进一步,用过pyinstaller把脚本打包成独立的.exe文件,这样连Python环境都不需要就可以运行。这一步涉及打包工具的安装和配置,以后有机会单独写一篇细讲。但"打包成exe"这件事给我的启发是:一个自动化工具有两种用户——你自己和技术水平更低的其他人。你需要考虑怎么让后者也能用起来。
6. 脚本思维进阶:从整理文件到解决更大规模的问题
6.1 把"文件整理"抽象成"批量处理任务"
文件整理脚本跑通之后,我的关注点不再停留在这个具体工具上,而是开始思考背后的方法论。这个脚本本质上是在处理什么问题?答案是:根据某种规则,对一批同类对象执行分类、移动、删除等操作。
这个抽象一旦建立起来,你会发现它可以应用到很多领域:
- 批量重命名一组以特定模式命名的照片,比如把
IMG_1234.JPG改成2024_05_15_旅游_1234.JPG - 根据Excel表格里的数据批量生成文本报告
- 每隔一段时间检查某个网站的订单信息,有变化就发邮件通知
- 监听某个文件夹,有新文件进入就自动调用某个处理流程
我自己后来就基于这个脚本的骨架,写了另一个批量处理图片的脚本:把某个目录下所有超过2MB的图片自动压缩,并生成缩略图。代码大概只用了原本脚本改改,但解决了另一个岗位同学"图片太大传不上系统"的长期痛点。
这就是"脚本思维"的本质——识别出重复性、规则性、批量性这三个特征的任务,然后考虑用代码去替代手工作业。
6.2 一个实用的自动化清单:如何判断任务值不值得写脚本
我发现很多人不是不会写脚本,而是不知道什么任务值得写、什么任务不值得。为了这个问题,我给自己整理了一个小清单:
- 任务是否重复出现?每周至少一次算及格,每天一次算优秀。
- 是否规则明确?如果"看到A文件我顺手处理一下",这种顺手行为规则其实不明确,写脚本难度大。
- 执行一次耗时多久?耗时超过1分钟且每月重复5次以上,就值得自动化。
- 出错成本高不高?如果误操作会破坏重要数据,那自动化前必须加入安全机制。
- 是否涉及多个步骤和多个判断?步骤越多的任务,自动化价值越大,因为人容易疲劳和出错。
拿这个清单来评估"整理下载目录"这个任务,它完美命中所有条件。后来我还遇到过"每周汇总一下各小组交上来的报销单统计信息",也是用类似思路做的自动化脚本——定期读取文件夹里的Excel,用pandas统计数据后生成汇总表。
6.3 Python脚本的"下一步":从脚本到小系统
当你的自动化脚本超过三五个之后,它们会逐渐形成一个"个人自动化系统":整理文件的、生成日志的、定期发提醒的、批量处理报表的。这时候,下一个值得思考的问题就是:怎么让它们配合起来?
比如说,文件整理脚本跑完后,可以通过os.startfile在Windows上自动打开整理完毕的"图片"文件夹,方便你快速预览;也可以把整理结果追加到一份每天的日志Markdown里,形成个人工作日报。我后来就是这么扩展的——我的电脑上每天早晨会运行两个脚本,一个整理下载目录,另一个读取整理结果生成昨天的工作摘要,自动打印到终端里。
从"一个脚本解决一个问题"到"几个脚本配合解决一批问题",这中间的跨越靠的其实就是两件事:一是像模块一样由小到大组织代码,二是给自己的脚本写好说明文档。你可能觉得写README是多余的动作,但实际维护几个脚本后你会发现,没有说明的脚本,三个月后你看它就像别人写的代码一样陌生。
这也就是为什么我在这篇文章开头强调"把需求写下来"——因为脚本不仅是电脑上运行的代码,也是你未来记忆的载体。它替你记住的是那些规则和流程,而你只需要记住它是什么、能干什么、怎么调用就足够了。