阅读(3856) (12)

GoFrame 命令管理-命令行对象

2022-03-28 15:18:21 更新

基本介绍

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

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

// 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