支持子命令命令行程序支持包开发

it2023-02-10  73

服务计算Homework05

项目地址使用说明:执行命令go install "hw05"后即可使用hw05作为根命令执行对应操作,如hw05 time、hw05 -h、hw05 -n yourName -a yourAge等等

课程任务

了解 Cobra包,使用 cobra 命令行生成一个简单的带子命令的命令行程序模仿 cobra.Command 编写一个 myCobra 库将带子命令的命令行处理程序的 import (“github.com/spf13/cobra”) 改为 import (corbra “gitee.com/yourId/yourRepo”)使得命令行处理程序修改代价最小,即可正常运行

任务要求

1. 核心任务,就是模仿 cobra 库的 command.go 重写一个 Command.go

仅允许使用的第三方库 flag "github.com/spf13/pflag"可以参考、甚至复制原来的代码必须实现简化版的 type Command struct 定义和方法不一定完全兼容 github.com/spf13/cobra可支持简单带子命令的命令行程序开发

2. 包必须包括以下内容:

生成的中文 api 文档有较好的 Readme 文件,包括一个简单的使用案例每个go文件必须有对应的测试文件

实现过程

文件结构

. ├── hw05 │ └── cmd │ │ └── root.go │ │ └── myTime.go │ └── myCobra │ │ └── command.go │ │ └── command_test.go │ └── main.go └── ...

Croba包简介

提供功能

简易的子命令行模式,如 app server, app fetch等等完全兼容posix命令行模式嵌套子命令subcommand支持全局,局部,串联flags使用Cobra很容易的生成应用程序和命令,使用cobra create appname和>cobra add cmdname如果命令输入错误,将提供智能建议,如 app srver,将提示srver没有,是否是app server自动生成commands和flags的帮助信息自动生成详细的help信息,如app help自动识别-h,–help帮助flag自动生成应用程序在bash下命令自动完成功能自动生成应用程序的man手册命令行别名自定义help和usage信息可选的紧密集成的viper apps Command 结构体主要字段 Use:命令名Short & Long:帮助信息的文字内容Run:运行命令的逻辑

Command 结构体中的字段当然远不止这些,受限于篇幅,这里无法全部介绍,有兴趣的自行查阅官方文档

Cobra 结构由三部分组成 命令 (commands)参数 (arguments)标志 (flags)

用git举例如下

git clone URL --bare // git:根命令 // clone:子命令 // URL:参数,即 clone 作用的对象 // --bare:标志

实现没有子命令的CLIs程序

root.go,根据Command结构体定义一个命令如下,实现简单的打印Hello world

package cmd import ( "fmt" "os" "github.com/spf13/cobra" ) var rootCmd = &cobra.Command{ Use: "test", Short: "a testing of command", Long: `reference: https://github.com/spf13/cobra`, Run: func(cmd *cobra.Command, args []string) { // 执行程序 fmt.Println("Hello world") }, } func Execute() { err := rootCmd.Execute() if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } }

main.go,调用新建的命令

package main import ( "hw05/cmd" ) func main() { // 调用cmd包中的Execute()函数 cmd.Execute() }

运行结果如下

实现含有子命令的CLIs程序

myTime.go,定义子命令为time,获取并打印当前时间

package cmd import ( "fmt" "time" "github.com/spf13/cobra" ) var ( timeCmd = &cobra.Command{ Use: "time", Short: "get current time", Long: `subcommand which returns current time`, Run: func(cmd *cobra.Command, args []string) { // Do Stuff Here fmt.Println(time.Now()) }, } )

在root.go中添加init()函数,将新定义的timeCmd命令结构定义为rootCmd的子命令

func init() { // 命令的层级关系 rootCmd.AddCommand(timeCmd) }

main.go不变,使用方法为根命令 子命令即hw05 time

实现含有标志的CLIs程序

cobra自带标志-h

标志分为Persistent Flags和Local Flags,相应的添加命令如下

// 持久性标志 rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output") // 局部标志 localCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")

将root.go修改如下

package cmd import ( "fmt" "os" "github.com/spf13/cobra" ) var name string var age int var rootCmd = &cobra.Command{ Use: "test", Short: "a testing of command", Long: `reference: https://github.com/spf13/cobra`, Run: func(cmd *cobra.Command, args []string) { // 执行程序 // fmt.Println("Hello world") if name != "" && age != 0 { fmt.Print("Hello ", name, ", a ", age, " years old student") } }, } func init() { // 命令的层级关系 rootCmd.AddCommand(timeCmd) rootCmd.Flags().StringVarP(&name, "name", "n", "", "person's name") rootCmd.Flags().IntVarP(&age, "age", "a", 0, "person's age") } func Execute() { if err := rootCmd.Execute(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } }

结果如下图:

command.go的实现

参考官方代码实现,实现的是简化版,具体代码见项目。部分实现的函数说明如下:

Command结构体:存储Use、Short、Long、Run等基本信息 type Command struct { // 命令名称,根命令可任意取 Use string // 命令描述,-h显示的帮助信息 Short string Long string // 执行的操作 Run func(cmd *Command, args []string) // 存储命令 commands []*Command // 子命令 subCom *Command // 父命令 parentCom *Command // 命令参数 args []string // 标志选项 pflags *flag.FlagSet } AddCommand方法:添加子命令 func (c *Command) AddCommand(cmds ...*Command) { for _, x := range cmds { if x == c { panic("Command can't be a child of itself") } x.parentCom = c c.commands = append(c.commands, x) } } Execute、execute方法:执行相应命令操作,即对应Run函数 func (c *Command) Execute() error { if c == nil { panic("Called Execute() on a nil Command") } // 执行到根命令 if c.parentCom == nil { ParseArgs(c, os.Args[1:]) } c.execute() return nil } func (c *Command) execute() { // 子命令为空则执行自身 if c.subCom == nil { if c.parentCom == nil { for _, v := range c.args { if v == "-h" || v == "--help" { c.Print_help() return } } } c.Run(c, c.args) return } c.subCom.execute() } ParseArgs方法:获取对应参数 func ParseArgs(c *Command, args []string) { if len(args) < 1 { return } for _, v := range c.commands { // 找到当前命令 if v.Use == args[0] { c.args = args[:1] c.subCom = v v.parentCom = c ParseArgs(v, args[1:]) return } } c.args = args // 添加选项参数 c.PersistentFlags().Parse(c.args) } PersistentFlags方法:添加对应标志/选项 func (c *Command) PersistentFlags() *flag.FlagSet { if c.pflags == nil { c.pflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) } return c.pflags }

将root.go和myTime.go中的import "github.com/spf13/cobra"改为import cobra "hw05/myCobra"。执行结果如下

测试command.go,详见项目

API

参考资料 Croba的简单使用 golang命令行库cobra的使用

最新回复(0)