如何为脚本添加异常处理?

1 人参与

凌晨三点,服务器告警短信疯狂轰炸手机,睡眼惺忪地爬起来排查,却发现是因为一个简单的文件读取操作没做判断,导致整个运维脚本直接崩溃,后续的清理任务全都没执行。这种"一颗老鼠屎坏了一锅粥"的场景,在脚本编写中太常见了。很多人写脚本时习惯顺着理想情况思考:文件存在、网络通畅、命令执行成功。但现实往往很骨感,磁盘满了、网络抖了、权限不够了,任何一个环节掉链子,脚本如果缺乏异常处理,就会像个没头苍蝇一样乱撞,或者直接躺平摆烂。

异常处理的层级逻辑

给脚本加异常处理,不是为了把代码包裹在 try-catch 里就完事了,这是一种分层防御的策略。

如何为脚本添加异常处理?

第一层是预判。在执行关键操作前,先检查前置条件。比如要写入文件,先检查磁盘空间够不够;要请求 API,先验证 URL 格式对不对。这就像开车前先看油表,而不是等半路熄火了才后悔。

第二层是捕获与隔离。当代码真的报错了,不能让它像病毒一样扩散。用 try-except 块把可能出错的代码段隔离起来,一旦捕获到异常,脚本还能保持清醒,而不是直接吐出一堆报错堆栈然后退出。这时候可以记录日志,或者执行备选方案,比如主服务器连不上,自动切到备用服务器试试。

Python 脚本中的实战技巧

在 Python 运维脚本中,很多人喜欢用 except Exception as e 这种"一刀切"的方式,其实这挺危险的。它会把所有错误都吞进去,包括你想都没想过的系统级错误。正确的姿势应该是精确捕获

比如网络请求,就应该专门捕获 requests.Timeoutrequests.ConnectionError。如果是文件操作,就要盯着 FileNotFoundErrorPermissionError。这样当真正的未知错误发生时,脚本依然会报错,而不是被悄悄吞掉,留下一堆难以排查的隐患。

try:
    response = requests.get(url, timeout=5)
    response.raise_for_status()
except requests.Timeout:
    logging.error(f"请求超时:{url}")
    # 执行重试逻辑
except requests.HTTPError as e:
    logging.error(f"HTTP 错误:{e.response.status_code}")
    # 触发告警通知

别忘了资源清理

异常处理还有一个容易被忽视的侧面:资源清理。脚本打开了一个数据库连接,或者写了一半临时文件,突然抛异常退出了,这些资源就成了孤儿。这就是 finally 子句存在的意义。不管代码是跑通了还是崩掉了,finally 里的代码一定会执行,用来关闭连接、删除临时文件、释放锁,把战场打扫干净。

重试机制的艺术

有些错误是暂时的,比如网络抖动。这时候直接放弃太可惜,加个重试机制往往能救命。不过重试不是死循环,得有策略。指数退避是个好办法:第一次等 1 秒,第二次等 2 秒,第三次等 4 秒……既给了服务恢复的时间,又不会把服务器请求淹没。设置个最大重试次数,比如 3 次,超过了再认栽。

一个健壮的脚本,应该像个有经验的运维工程师:预判风险、隔离故障、留好后路、清理现场。把这套逻辑刻进代码里,半夜被电话叫醒的概率,自然就下去了。

参与讨论

1 条评论
  • 暗夜猎刃

    半夜被叫醒太真实了,上次也是没判空,直接炸了。

    回复