Python 脚本自动化入门记录
我最早学 Python 不是为了写什么大项目,动机特别朴素:每天有一堆手动重复的破事,烦。整理文件、改名字、统计表格里的数、定时备份——这些活单次做只要几分钟,但每天都做,一年下来就是几百个小时。这篇文章用一个我真实用过的例子,把"从手动到自动"这条路完整走一遍。我会把中间踩的坑也写上,因为那些坑才是真正卡住人的地方。
一、自动化的本质:把重复动作变成循环
想通这一层之后,写脚本这件事的门槛一下子降下来了。任何"对一堆东西做同一件事"的工作,结构都是一样的三步,没有例外:
我第一次意识到这点,是在手动改了两百多个文件名之后。当时我的做法是:全选、右键重命名、输入新名字、确定,然后发现自己漏了几十个,又重新来一遍。做完那一刻我就在想——这件事机器做只要一秒钟,我为什么要花四十分钟?
二、第一个真正用上的脚本
需求很简单:把一批 photo_001.jpg 这样的文件,统一加上日期前缀,让它按时间顺序排得整整齐齐。
import os
folder = "./photos"
prefix = "20260925_"
for name in os.listdir(folder):
old = os.path.join(folder, name)
if not os.path.isfile(old):
continue # 只处理文件,跳过子目录
new = os.path.join(folder, prefix + name)
os.rename(old, new)
print(f"已重命名: {name} -> {prefix + name}")
十行代码,但是第一次跑的时候我没敢直接跑。我先把它改成了只打印不执行:
# 第一遍:只打印,不动文件
for name in os.listdir(folder):
print(f"将要重命名: {name} -> {prefix + name}")
# 确认打印出来的东西就是我想的那样之后,
# 再把上面的 print 换成真正的 os.rename
三、从"能跑"到"敢用",要补的四件事
上面那个十行脚本能解决一次问题,但你不会想用它第二次——因为路径和前缀都写死在代码里,每次用都要改代码。想让脚本真正变成能反复用的东西,要补四件事:
- 把会变的东西提到外面。路径、前缀、日期格式这些每次都可能不同的值,改成从命令行参数拿,而不是写死在代码里。改参数不改代码,这是脚本和一次性代码的分界线。
- 加一个干跑开关。给它一个
--dry-run参数,打开就只打印计划、不真的动手。这样你每次用它之前都能先看一眼它打算干什么,安心。 - 让它留下记录。每次跑完往一个日志文件里追加一行:什么时间、处理了多少个、有没有失败。出问题的时候,这份记录比你的记忆可靠得多。
- 把可能的意外接住。某个文件被占用、权限不够、编码不是 UTF-8——这些都会让脚本中途崩掉。用
try/except把单个文件的失败接住、记下来、继续处理下一个,别让一个坏文件毁掉整批任务。
四、一个更完整的版本
把上面四条都加进去之后,脚本大概长这样。代码长了一倍,但从此我就再也不用打开它改代码了:
import os
import sys
import argparse
from datetime import datetime
def main():
p = argparse.ArgumentParser(description="给文件批量加日期前缀")
p.add_argument("folder", help="要处理的目录")
p.add_argument("--prefix", default=datetime.now().strftime("%Y%m%d_"),
help="前缀,默认是今天的日期")
p.add_argument("--dry-run", action="store_true", help="只打印,不真改")
args = p.parse_args()
if not os.path.isdir(args.folder):
print(f"目录不存在: {args.folder}")
sys.exit(1)
ok, fail = 0, 0
for name in sorted(os.listdir(args.folder)):
old = os.path.join(args.folder, name)
if not os.path.isfile(old):
continue
new = os.path.join(args.folder, args.prefix + name)
if os.path.exists(new):
print(f"跳过(目标已存在): {name}")
fail += 1
continue
try:
if args.dry_run:
print(f"[干跑] {name} -> {args.prefix + name}")
else:
os.rename(old, new)
print(f"已处理: {name}")
ok += 1
except OSError as e:
print(f"失败: {name} ({e})")
fail += 1
print(f"\n完成:成功 {ok} 个,跳过/失败 {fail} 个")
if __name__ == "__main__":
main()
用法就很舒服了:
# 先看它打算干嘛
python rename.py ./photos --dry-run
# 确认没问题再真跑
python rename.py ./photos
# 想换前缀就传参数,不用改代码
python rename.py ./photos --prefix "旅行_"
里面有两点值得单独说:一是 if os.path.exists(new) 那个判断,避免把已经处理过的文件再套一层前缀;二是 if __name__ == "__main__" 这个写法,它让这个文件既能直接跑,也能被别的脚本导入复用。
五、让它自己按时跑
脚本写好了,接下来是不用自己记得点它。Linux 上交给 cron:
# 打开当前用户的定时任务表
crontab -e
# 每天凌晨 3 点跑一次
0 3 * * * /usr/bin/python3 /opt/scripts/backup.py >> /var/log/backup.log 2>&1
# 工作日每 2 小时跑一次
0 */2 * * 1-5 /usr/bin/python3 /opt/scripts/sync.py
五个位置的顺序是:分 时 日 月 周,后面接要执行的命令。Windows 上对应的是"任务计划程序",思路完全一样——定时间、指脚本、设好工作目录。
另一个建议:定时任务里跑的命令,一定要把输出重定向到日志文件(就是上面 >> 和 2>&1 那部分)。不重定向的话,脚本半夜失败了你是不会知道的——什么都没有发生,和"跑成功了"看起来一模一样。
六、我给自己定的几条规矩
会做的
- 先手动跑通逻辑,再套循环
- 涉及改删的先干跑一遍
- 文件开头写清用途和用法
- 把路径当参数传进去
- 跑完打印一行摘要
不去做的
- 一上来就写全自动的复杂流程
- 在脚本里硬编码一堆路径
- 不留任何输出,失败了还不知道
- 为了"优雅"把简单事情写复杂
- 在没有备份的目录上直接试
小结
Python 让我最舒服的一点是:它允许我用几十行解决一个具体问题,而不强迫我为了解决方案去搭一整套工程。一个脚本只要解决了你真实的麻烦,它就有价值,跟它有多少行、用了多高级的技巧完全无关。我现在判断一个脚本好不好的标准只有一条——下次遇到同样的活,我会不会想起它、愿不愿意用它。