脚本退出码非0会导致自动化流程中断;应显式用sys.exit(n)返回带业务语义的码值(如err_file_missing=10),并检查subprocess.run().returncode或使用check=true。

脚本退出码不为 0 时,自动化流程就停了
linux/unix 下的 shell 脚本、CI/CD 流水线(如 github Actions、jenkins)、crontab 任务,都依赖 sys.exit() 或隐式退出码判断 python 脚本是否“成功”。退出码非 0(比如 sys.exit(1))会被上层当作失败信号,后续命令不再执行——这不是 Python 的约定,是操作系统级行为。
常见错误现象:python myscript.py && echo "done" 中,即使脚本里 print 了成功日志,只要退出码是 1,echo 就不会运行。
- 显式调用
sys.exit(n)是最可控的方式;直接 return 或抛出未捕获异常也会产生非 0 退出码(通常是 1),但含义模糊 - 避免用
os._exit():它绕过清理逻辑(如atexit注册函数),只在 fork 子进程等极少数场景用 - 标准退出码含义建议遵守:0 表示成功;1–125 用于应用自定义错误(如 2 表示命令行参数错,126–127 留给 shell 保留)
如何让不同错误类型对应不同退出码
靠单一的 sys.exit(1) 区分不了“文件不存在”和“网络超时”,而自动化系统可能需要据此触发不同告警或重试策略。关键不是“有没有退出码”,而是“码值是否有业务语义”。
使用场景:运维脚本检测服务状态,返回 10 表示端口不通,20 表示响应超时,30 表示认证失败——上游监控工具可按码值路由告警通道。
立即学习“Python免费学习笔记(深入)”;
- 定义清晰的错误码常量,比如
ERR_FILE_MISSING = 10、ERR_TIMEOUT = 20,别用魔数 - 在异常处理分支中分别调用
sys.exit(ERR_FILE_MISSING),而不是统一兜底sys.exit(1) - 注意:Python 自身异常(如
ValueError)不会自动映射到特定退出码,必须手动捕获并 exit
subprocess.run() 拿不到 Python 脚本的退出码?
不是拿不到,是默认行为会吞掉——如果没设 check=True 或没检查 returncode 属性,脚本失败了你也完全不知情。
常见错误现象:用 subprocess.run(["python", "task.py"]) 调用子脚本,子脚本 sys.exit(2) 了,但父脚本继续往下跑,像什么都没发生。
- 务必检查结果对象的
returncode字段:result = subprocess.run(...); if result.returncode != 0: ... - 加
check=True可让失败时直接抛subprocess.CalledProcessError,适合“失败必须中断”的场景 - 别依赖
stdout或打印日志判断成败——输出可能被重定向、截断,或脚本根本没 print
Shell 里怎么测试 Python 脚本的退出码
写完脚本别急着扔进 crontab,先在终端验证退出行为是否符合预期。靠 echo $? 看上一条命令的退出码,是最直接的调试手段。
示例:python -c "import sys; sys.exit(5)"; echo $? → 输出 5;python -c "print('ok')"; echo $? → 输出 0。
- 测试时用
-c最快,避免反复改文件、保存、执行 - 注意:交互式 Python 解释器里敲
exit(1)是调用内置函数,不是进程退出,$?不会变;必须用sys.exit()或执行独立脚本 - 在 CI 配置里(如
.github/workflows/ci.yml),记得把关键步骤的退出码检查逻辑写进 shell 命令,别只信 Python 里的 print
退出码本身很简单,但一旦混入管道、重定向、shell 函数封装、容器启动命令(如 ENtryPOINT),就容易被中间层覆盖或忽略。最常被漏掉的是:在 try/except 里吞了异常却忘了 exit,或者用 os.system() 替代 subprocess.run() 导致退出码解析失真。