基本介绍

大部分场景下,我们通过 Command 命令行对象来管理单个或多个命令,并且使用默认的命令行解析规则(不用显式使用 Parser 解析器)即可。 Command 对象定义如下:

详细请参考接口文档: https://pkg.go.dev/github.com/gogf/gf/v2/os/gcmd@master#Command

  1. // Command holds the info about an argument that can handle custom logic.
  2. type Command struct {
  3. Name string // Command name(case-sensitive).
  4. Usage string // A brief line description about its usage, eg: gf build main.go [OPTION]
  5. Brief string // A brief info that describes what this command will do.
  6. Description string // A detailed description.
  7. Arguments []Argument // Argument array, configuring how this command act.
  8. Func Function // Custom function.
  9. FuncWithValue FuncWithValue // Custom function with output parameters that can interact with command caller.
  10. HelpFunc Function // Custom help function
  11. Examples string // Usage examples.
  12. Additional string // Additional info about this command, which will be appended to the end of help info.
  13. Strict bool // Strict parsing options, which means it returns error if invalid option given.
  14. Config string // Config node name, which also retrieves the values from config component along with command line.
  15. parent *Command // Parent command for internal usage.
  16. commands []*Command // Sub commands of this command.
  17. }

由于对象均有详细的注释,这里不再赘述。

回调方法

Command 对象支持 3 个回调方法:

  • Func 我们一般需要自定义这个回调方法,用于实现当前命令执行的操作。
  • FuncWithValue 方法同 Func,只不过支持返回值,往往用于命令行相互调用的场景,一般项目用不到。
  • HelpFunc 自定义帮助信息,一般来说没有太大必要,因为 Command 对象能够自动生成帮助信息。

我们主要关注 Func 回调方法即可,其他方法大家感兴趣可以自行研究。

Func 回调

方法定义:

  1. // Function is a custom command callback function that is bound to a certain argument.
  2. type Function func(ctx context.Context, parser *Parser) (err error)

可以看到,在回调方法内部,我们通过 parser 对象获取解析参数和选项,并通过返回 error 来告诉上层调用方法是否执行成功。

使用示例:

  1. package main
  2. import (
  3. "context"
  4. "github.com/gogf/gf/v2/frame/g"
  5. "github.com/gogf/gf/v2/net/ghttp"
  6. "github.com/gogf/gf/v2/os/gcmd"
  7. "github.com/gogf/gf/v2/os/gctx"
  8. )
  9. var (
  10. Main = &gcmd.Command{
  11. Name: "main",
  12. Brief: "start http server",
  13. Description: "this is the command entry for starting your http server",
  14. Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
  15. s := g.Server()
  16. s.BindHandler("/", func(r *ghttp.Request) {
  17. r.Response.Write("Hello world")
  18. })
  19. s.SetPort(8199)
  20. s.Run()
  21. return
  22. },
  23. }
  24. )
  25. func main() {
  26. Main.Run(gctx.New())
  27. }

这也是大部分项目的启动命令行对象的样子,大部分项目只有一个启动入口,并且只会有一个回调方法实现。

帮助信息生成

Command 对象虽然可以自定义 HelpFunc 帮助回调方法,但 Command 对象可以自动生成 Help 使用帮助信息,大部分场景下无需自定义。并且 gcmd 组件默认内置了支持了 h/help 选项,因此使用 gcmd 组件的程序可以通过这两个选项自动生成 Help 帮助信息。

我们来看一个例子,我们先通过 go build main.go 把上面的例子编译为二进制 main 文件,然后来简单看一下只有一个命令场景下自动生成的帮助信息:

  1. $ ./main -h
  2. USAGE
  3. main [OPTION]
  4. DESCRIPTION
  5. this is the command entry for starting your http server

层级命令管理

父命令与子命令

一个 Command 命令可以添加子级命令,当 Command 存在子级命令时,自己便成为了父级命令。子级命令也可以添加自己的子级命令,以此类推,形成层级命令关系。父级命令和子级命令都可以有自己的回调方法,不过大部分场景下,一旦 Command 成为了父级命令,回调方法往往都没有太大存在的必要。我们通常通过 AddCommand 方法为 Command 添加子级命令:

  1. // AddCommand adds one or more sub-commands to current command.
  2. func (c *Command) AddCommand(commands ...*Command) error

层级命令使用示例

我们来演示一个多命令管理的示例。我们将上面的例子改进一下,增加两个子级命令。

  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/gogf/gf/v2/os/gcmd"
  6. "github.com/gogf/gf/v2/os/gctx"
  7. )
  8. var (
  9. Main = &gcmd.Command{
  10. Name: "main",
  11. Brief: "start http server",
  12. Description: "this is the command entry for starting your process",
  13. }
  14. Http = &gcmd.Command{
  15. Name: "http",
  16. Brief: "start http server",
  17. Description: "this is the command entry for starting your http server",
  18. Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
  19. fmt.Println("start http server")
  20. return
  21. },
  22. }
  23. Grpc = &gcmd.Command{
  24. Name: "grpc",
  25. Brief: "start grpc server",
  26. Description: "this is the command entry for starting your grpc server",
  27. Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
  28. fmt.Println("start grpc server")
  29. return
  30. },
  31. }
  32. )
  33. func main() {
  34. err := Main.AddCommand(Http, Grpc)
  35. if err != nil {
  36. panic(err)
  37. }
  38. Main.Run(gctx.New())
  39. }

可以看到,我们通过 AddCommand 命令为主命令增加了两个子级命令 http/grpc,分别用于开启 http/grpc 服务。当存在子级命令式,父命令往往便没有 Func 回调定义的必要了,因此我们这里去掉了 main 命令的 Func 定义。

我们编译后来执行一下看看效果:

  1. $ main
  2. USAGE
  3. main COMMAND [OPTION]
  4. COMMAND
  5. http start http server
  6. grpc start grpc server
  7. DESCRIPTION
  8. this is the command entry for starting your process

使用 http 命令:

  1. $ main http
  2. start http server

使用 grpc 命令:

  1. $ main grpc
  2. start grpc server