上一节课,我们开始把框架向工业级迭代,重新规划了目录,这一节课将对框架做更大的改动,让框架支持命令行工具。
第三方命令行工具库 cobra
obra 不仅仅能让我们快速构建一个命令行,它更大的优势是能更快地组织起有许多命令行工具,因为从根命令行工具开始,cobra 把所有的命令按照树形结构组织起来了。
cobra库最核心的数据结构Command:
一个 Command 代表一个执行命令。这个 Command 包含很多可设置的字段,如何使用这个 Command,就取决于我们如何设置这些属性。下面是源码片段,我在注释中列出了这些属性的意义。
// Command代表执行命令的结构
type Command struct {
// 代表当前命令的,如何执行,root 最好和生成的命令工具名称一致
Use string
// 代表这个工具的别名,在 subCommand 中有用,比如 root cmd1 和 root cmd_1 想要都执行一个 subCommand 就需要这样
Aliases []string
// 由于不强制设置,用于输入错误的时候建议字段
SuggestFor []string
// 这个就是在 help 的时候一句话描述这个命令的功能
Short string
// 详细描述这个命令的功能
Long string
// 例子
Example string
// 需要验证的参数
ValidArgs []string
// 有多少个参数,这里放了一个验证函数,可以是 ExactArgs,MaximumNArgs 等,验证有多少个参数
Args PositionalArgs
// 参数别名
ArgAliases []string
// 自动生成的命令设置
BashCompletionFunction string
// 如果这个命令已经废弃了,那么就这里写上废弃信息
Deprecated string
// 如果这个命令要被隐藏,设置这个字段
Hidden bool
// Annotations are key/value pairs that can be used by applications to identify or
// group commands.
Annotations map[string]string
// 这个命令的版本
Version string
// 是否要打印错误信息
SilenceErrors bool
// 是否要打印如何使用
SilenceUsage bool
// 是否有 flag,如果这个命令没有 flag,设置为 true,那么所有的命令后面的参数都会是 arguments
DisableFlagParsing bool
// 是否打印自动生成字样: ("Auto generated by spf13/cobra...")
DisableAutoGenTag bool
// 是否显示[flags]字样
DisableFlagsInUseLine bool
// 是否打印建议
DisableSuggestions bool
// 两个字符串的差距多少会进入 suggest
SuggestionsMinimumDistance int
// 是否使用 Traverse 的方式来解析参数
TraverseChildren bool
// 解析错误白名单, 比如像未知参数
FParseErrWhitelist FParseErrWhitelist
// The *Run 函数运行顺序:
// * PersistentPreRun()
// * PreRun()
// * Run()
// * PostRun()
// * PersistentPostRun()
// 会被继承的前置 Run
PersistentPreRun func(cmd *Command, args []string)
// 会被继承的前置 Run, 带 error
PersistentPreRunE func(cmd *Command, args []string) error
// 当前这个命令的前置 Run
PreRun func(cmd *Command, args []string)
// 当前这个命令的前置 Run,带 Error
PreRunE func(cmd *Command, args []string) error
// zh: 实际跑的时候运行的函数
Run func(cmd *Command, args []string)
// zh: Run 执行错误了之后
RunE func(cmd *Command, args []string) error
// 后置运行
PostRun func(cmd *Command, args []string)
// 后置运行,带 error
PostRunE func(cmd *Command, args []string) error
// 会被继承的后置运行
PersistentPostRun func(cmd *Command, args []string)
// 会被继承的后置运行,带 error
PersistentPostRunE func(cmd *Command, args []string) error
}
具体使用的一个场景:
// InitFoo 初始化 Foo 命令
func InitFoo() *cobra.Command {
FooCommand.AddCommand(Foo1Command)
return FooCommand
}
// FooCommand 代表 Foo 命令
var FooCommand = &cobra.Command{
Use: "foo",
Short: "foo 的简要说明",
Long: "foo 的长说明",
Aliases: []string{"fo", "f"},
Example: "foo 命令的例子",
RunE: func(c *cobra.Command, args []string) error {
container := c.GetContainer()
log.Println(container)
return nil
},
}
// Foo1Command 代表 Foo 命令的子命令 Foo1
var Foo1Command = &cobra.Command{
Use: "foo1",
Short: "foo1 的简要说明",
Long: "foo1 的长说明",
Aliases: []string{"fo1", "f1"},
Example: "foo1 命令的例子",
RunE: func(c *cobra.Command, args []string) error {
container := c.GetContainer()
log.Println(container)
return nil
},
}
- Use 代表这个命令的调用关键字,比如要调用 Foo1 命令,我们就要用 ./hade foo foo1 。Short 代表这个命令的简短说明,它会出现在上级命令的使用文档中。
- Long 代表这个命令的长说明,它会出现在当前命令的使用文档中。
- Aliases 是当前命令的别名,等同于 Use 字段;
- Example 是当前命令的例子,也是显示在当前命令的使用文档中。
而 RunE 代表当前命令的真正执行函数:
RunE: func(c *cobra.Command, args []string) error
这个执行函数的参数有两个:一个是 cobra.Command,表示当前的这个命令;而第二个参数是 args,表示当前这个命令的参数,返回值是一个 error,代表命令的执行成功或者失败。
如何使用命令行cobra
首先,引入到库,在framework目录下创建cobra目录,将v1.2.1版本的源码放置进去,删去其中的go.mod和go.sum, 然后全文替换github.com/spf13.cobra为自己的项目地址。
为了让cmd命令执行时从参数command中获取到容器,进而从服务容器获取到服务实例,我们将服务容器嵌入到Command结构中,因此将服务器容器挂载到根Command上,所有子命令通过Root()方法获取。
最后,使用命令行后,http服务的启动也是通过command的命令执行的,为了从容器中获取gin.engine,需要将engine也作为一个服务注册到容器中。
【小结】:
- cobra逐层构建command,运行通过RuneE函数
- 将gin.Engine和上节的app目录结构,都作为服务提供者注入到服务容器中