基本介绍

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

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

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

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

回调方法

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

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

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

Func回调

方法定义:

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

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

使用示例:

package main

import (
	"context"

	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/gogf/gf/v2/os/gcmd"
	"github.com/gogf/gf/v2/os/gctx"
)

var (
	Main = &gcmd.Command{
		Name:        "main",
		Brief:       "start http server",
		Description: "this is the command entry for starting your 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 world")
			})
			s.SetPort(8199)
			s.Run()
			return
		},
	}
)

func main() {
	Main.Run(gctx.New())
}

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

帮助信息生成

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

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

$ ./main -h 
USAGE
    main [OPTION]

DESCRIPTION
    this is the command entry for starting your http server  

层级命令管理

父命令与子命令

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

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

层级命令使用示例

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

package main

import (
	"context"
	"fmt"

	"github.com/gogf/gf/v2/os/gcmd"
	"github.com/gogf/gf/v2/os/gctx"
)

var (
	Main = &gcmd.Command{
		Name:        "main",
		Brief:       "start http server",
		Description: "this is the command entry for starting your process",
	}
	Http = &gcmd.Command{
		Name:        "http",
		Brief:       "start http server",
		Description: "this is the command entry for starting your http server",
		Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
			fmt.Println("start http server")
			return
		},
	}
	Grpc = &gcmd.Command{
		Name:        "grpc",
		Brief:       "start grpc server",
		Description: "this is the command entry for starting your grpc server",
		Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
			fmt.Println("start grpc server")
			return
		},
	}
)

func main() {
	err := Main.AddCommand(Http, Grpc)
	if err != nil {
		panic(err)
	}
	Main.Run(gctx.New())
}

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

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

$ main     
USAGE
    main COMMAND [OPTION]

COMMAND
    http    start http server
    grpc    start grpc server

DESCRIPTION
    this is the command entry for starting your process

使用http命令:

$ main http
start http server

使用grpc命令:

$ main grpc
start grpc server
Content Menu

  • No labels

4 Comments

  1. 开头第一段的”···不用显示使用···“是”显示“还是”显式“呢?

  2. 层级命令使用示例 gf run main.go 加参数

    $ main     
    USAGE
        main COMMAND [OPTION]
    
    COMMAND
        http    start http server
        grpc    start grpc server
    
    DESCRIPTION
        this is the command entry for starting your process

    gf run main.go 使用gf run 自动编译时,如何添加http grpc 参数

    1. gf 工具更新到 v2.1.2 后已经支持 gf run 带参数了

      gf run main.go --args "http"
  3. 如何在子命令后增加自定义参数呢,使用了flag.Parse()会覆盖框架默认的-h操作