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

第一层是预判。在执行关键操作前,先检查前置条件。比如要写入文件,先检查磁盘空间够不够;要请求 API,先验证 URL 格式对不对。这就像开车前先看油表,而不是等半路熄火了才后悔。
第二层是捕获与隔离。当代码真的报错了,不能让它像病毒一样扩散。用 try-except 块把可能出错的代码段隔离起来,一旦捕获到异常,脚本还能保持清醒,而不是直接吐出一堆报错堆栈然后退出。这时候可以记录日志,或者执行备选方案,比如主服务器连不上,自动切到备用服务器试试。
Python 脚本中的实战技巧
在 Python 运维脚本中,很多人喜欢用 except Exception as e 这种"一刀切"的方式,其实这挺危险的。它会把所有错误都吞进去,包括你想都没想过的系统级错误。正确的姿势应该是精确捕获。
比如网络请求,就应该专门捕获 requests.Timeout 和 requests.ConnectionError。如果是文件操作,就要盯着 FileNotFoundError 和 PermissionError。这样当真正的未知错误发生时,脚本依然会报错,而不是被悄悄吞掉,留下一堆难以排查的隐患。
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 次,超过了再认栽。
一个健壮的脚本,应该像个有经验的运维工程师:预判风险、隔离故障、留好后路、清理现场。把这套逻辑刻进代码里,半夜被电话叫醒的概率,自然就下去了。

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