You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

urfave/cli/v2中父Command如何初始化资源传递给Subcommands

实现方案

完全可以,不需要创建多个cli.App这么繁琐,用urfave/cli/v2原生的命令级Before钩子结合context.Context传值就能优雅实现。

核心逻辑

  • 父命令db的Before钩子会在所有下属子命令的Action执行前触发,此时父命令的--connect参数已经完成解析,可以直接用来初始化DB资源
  • 初始化完成的DB实例通过context.WithValue注入到上下文,子命令的Action直接从传入的*cli.Context中取出即可复用

代码示例

package main

import (
	"context"
	"log"
	"os"

	"github.com/urfave/cli/v2"
)

// 自定义非导出的上下文key类型,避免和其他包的key冲突
type ctxKey int
const dbCtxKey ctxKey = iota

func main() {
	app := &cli.App{
		Name: "mycmd",
		Commands: []*cli.Command{
			{
				Name:  "db",
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:     "connect",
						Required: true,
						Usage:    "数据库连接串",
					},
				},
				// 父命令前置钩子,所有子命令执行前都会先运行这段逻辑
				Before: func(c *cli.Context) error {
					connStr := c.String("connect")
					// 替换为实际的DB初始化逻辑
					db, err := initDB(connStr)
					if err != nil {
						return err
					}
					// 将初始化好的DB注入上下文
					c.Context = context.WithValue(c.Context, dbCtxKey, db)
					return nil
				},
				// 子命令如果需要释放资源,可以在这里加After钩子统一处理
				After: func(c *cli.Context) error {
					db := c.Context.Value(dbCtxKey).(*DB)
					// 替换为实际的DB关闭逻辑
					log.Println("关闭DB连接:", db.connStr)
					return nil
				},
				Subcommands: []*cli.Command{
					{
						Name: "create",
						Action: func(c *cli.Context) error {
							// 直接从上下文取复用的DB实例
							db := c.Context.Value(dbCtxKey).(*DB) // 这里的*DB替换为实际的DB实例类型
							// 替换为实际的create业务逻辑
							log.Println("执行create操作,使用连接:", db.connStr)
							return nil
						},
					},
					{
						Name: "update",
						Action: func(c *cli.Context) error {
							db := c.Context.Value(dbCtxKey).(*DB)
							// 替换为实际的update业务逻辑
							log.Println("执行update操作,使用连接:", db.connStr)
							return nil
						},
					},
				},
			},
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}

// 示例DB结构体和初始化方法,替换为实际实现
type DB struct {
	connStr string
}
func initDB(connStr string) (*DB, error) {
	log.Println("初始化DB连接:", connStr)
	return &DB{connStr: connStr}, nil
}

运行验证

执行命令mycmd db --connect "user:pass@tcp(127.0.0.1:3306)/test" create,输出顺序如下:

2024/xx/xx xx:xx:xx 初始化DB连接: user:pass@tcp(127.0.0.1:3306)/test
2024/xx/xx xx:xx:xx 执行create操作,使用连接: user:pass@tcp(127.0.0.1:3306)/test
2024/xx/xx xx:xx:xx 关闭DB连接: user:pass@tcp(127.0.0.1:3306)/test

DB只会初始化一次,所有子命令都可以复用同一个实例,资源释放也只需要统一写一次。

内容的提问来源于stack exchange,提问作者user1957145

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 03:06:06