gcmd 命令行:定义命令与获取参数

Go 服务常需要命令行入口:./app run./app migrate --force./app version。GoFrame v2 的 gcmd 组件(github.com/gogf/gf/v2/os/gcmd)统一了参数解析、命令定义与帮助信息,gf 命令行工具本身(如 gf gen dao)就是基于它实现的,这是官方文档给出的学习参照。

基本概念

  • 参数(Arg):不带横线前缀的位置参数,如 build main.go 中的 main.go;选项(Opt):-o=gf.exe-y--name=john 这类输入,位置可任意。

基础方法获取参数与选项

package main
import (
    "fmt"
    "github.com/gogf/gf/v2/os/gcmd"
)
func main() {
    gcmd.Init("gf", "build", "main.go", "-o=gf.exe", "-y") // 模拟命令行
    fmt.Println(gcmd.GetArg(0))              // gf,索引 0 是程序名
    fmt.Println(gcmd.GetArg(1))              // build
    fmt.Println(gcmd.GetArgAll())            // [gf build main.go]
    fmt.Println(gcmd.GetOpt("o"))            // gf.exe
    fmt.Println(gcmd.GetOpt("d", "defaultV")) // defaultV,不存在时用默认值
}

GetOpt(name) 在选项不存在时返回 nil,判断“是否带数据”用 GetOpt(name) != nilGetOptWithEnv("gf.debug") 先读命令行选项、读不到再读 GF_DEBUG 环境变量,适合“配置可被命令行覆盖”的场景。

用 Command 对象定义命令

命令多了之后,推荐建模成 gcmd.Command 对象,自动获得层级命令与帮助信息:

var (
    Main = &gcmd.Command{
        Name:        "main",
        Brief:       "start http server",
        Description: "command entry for starting http server",
        Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
            s := g.Server()
            s.BindHandler("/", func(r *ghttp.Request) {
                r.Response.Write("hello")
            })
            s.SetPort(8199)
            s.Run()
            return
        },
    }
)
func main() {
    Main.Run(gctx.New()) // 执行 ./app -h 可查看自动生成的帮助
}

回调签名固定为 func(ctx context.Context, parser *gcmd.Parser) error。子命令用 Main.AddCommand(httpCmd, grpcCmd) 挂载,父命令存在子命令时通常不再定义 Func,直接执行 ./app 会打印子命令列表与帮助。

结构化参数:像定义接口一样定义命令

选项一多,硬编码索引就难维护。官方提供“对象化命令 + 结构化输入参数”写法,标签支持 nameshortargbriefv(复用校验组件)等:

type cMain struct {
    g.Meta `name:"main"`
}

type cMainHttpInput struct {
    g.Meta  `name:"http" brief:"start http server"`
    Name    string `v:"required" name:"NAME" arg:"true" brief:"server name"`
    Port    int    `v:"required" short:"p" name:"port" brief:"port of http server"`
}
type cMainHttpOutput struct{}

func (c *cMain) Http(ctx context.Context, in cMainHttpInput) (out *cMainHttpOutput, err error) {
    // 参数已自动转换类型并完成 v 标签校验,直接使用 in.Name / in.Port
    g.Server(in.Name).SetPort(in.Port).Run()
    return
}

func main() {
    cmd, err := gcmd.NewFromObject(cMain{})
    if err != nil {
        panic(err)
    }
    cmd.Run(gctx.New()) // ./app http my-web -p 8199
}

漏传必填参数时会得到 “arguments validation failed for command” 的校验错误并打印堆栈,便于定位。

注意事项

  • 结构化入参的字段匹配“不区分大小写、忽略特殊字符”,如 Name 可被 name/NAME 命中。
  • gf 的所有子命令(gf initgf gen daogf build 等)都是该命令模型的实例,更多细节参考其源码与官方文档 goframe.org。

小结

先掌握 GetArg/GetOpt 快速解析,再进阶 gcmd.Commandgcmd.NewFromObject 结构化定义,命令行代码就能从“字符串硬解析”升级为“声明式、可校验、自动帮助”的工程写法。

笔记加载中…