
Click 的 @click.group() 默认不自动传播子命令的退出码;需通过 @click.pass_context 获取上下文对象,并调用 ctx.exit(code) 主动终止,以确保退出码准确返回 Shell。
click 的 `@click.group()` 默认不自动传播子命令的退出码;需通过 `@click.pass_context` 获取上下文对象,并调用 `ctx.exit(code)` 主动终止,以确保退出码准确返回 shell。
在 Click 中,@click.group() 本身并不捕获或转发子命令的退出状态——子命令若直接使用 sys.exit() 或抛出未处理异常,虽能终止进程,但会绕过 Click 的上下文清理机制(如 @click.pass_context 注册的 close() 回调、资源释放钩子等),导致潜在资源泄漏或行为不一致。
正确的做法是:所有需要控制退出码的命令(包括主 group 和子 command)均应显式接收并使用 click.Context 对象,并通过其 .exit(code) 方法退出。该方法会安全终止命令执行链,并将指定退出码传递给父级调用者(最终由 Click CLI 框架返回给 Shell)。
以下是推荐实现方式:
import click
@click.group()
@click.pass_context
def main(ctx: click.Context):
"""主命令组入口,无需主动 exit;仅需接收 ctx 即可支持子命令退出码传递。"""
pass
@main.command()
@click.pass_context
def get(ctx: click.Context):
"""模拟获取操作:成功返回 0,失败返回 1。"""
try:
# 模拟业务逻辑
result = fetch_data() # 假设此函数可能失败
click.echo("Data retrieved successfully.")
ctx.exit(0)
except Exception as e:
click.echo(f"Error: {e}", err=True)
ctx.exit(1)
def fetch_data():
# 示例:此处可抛出异常触发失败路径
raise RuntimeError("Network timeout")⚠️ 注意事项:
- 不要使用
sys.exit()替代ctx.exit()—— 它会跳过 Click 的上下文生命周期管理; -
@click.pass_context必须加在所有需要退出控制的命令上,且ctx参数必须是第一个形参; - 若子命令未显式调用
ctx.exit(),默认成功退出码为0; -
ctx.exit()接收任意整数,但建议遵循 POSIX 规范:0表示成功,非0(通常1)表示一般错误。
通过这种模式,你既能精准控制每个子命令的退出状态,又能保障 Click 框架的健壮性与可维护性。

















