golang程序包开发,复杂命令行支持

编程入门 行业动态 更新时间:2024-10-05 05:12:03

golang程序包开发,复杂<a href=https://www.elefans.com/category/jswz/34/1771327.html style=命令行支持"/>

golang程序包开发,复杂命令行支持

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

任务

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

查看Cobra包的readme。

Cobra is a library providing a simple interface to create powerful modern CLI interfaces similar to git & go tools.
Cobra is also an application that will generate your application scaffolding to rapidly develop a Cobra-based application.

可以看到,Cobra包提供了一个简单的接口,使得用户可以创建具有复杂功能的命令行程序。

Getting Started

为了使用Cobra包,先将其下载到本地。

go get -u github/spf13/cobra

然后在需要用到Cobra包的文件中import即可

import "github/spf13/cobra"

参考官方的readme教程,建立一个简易的组织结构如下:

Create rootCmd

在root.go中编写代码如下:
定义了一个根命令,cobra.Command是一个结构体,通过命令来执行Run函数。在此根命令中,通过执行Run函数可以打印一串字符串HAHAHA

package cmdimport ("os""fmt""github/spf13/cobra"
)var rootCmd = &cobra.Command{Use:   "hugo",Short: "Hugo is a very fast static site generator",Long: `A Fast and Flexible Static Site Generator built withlove by spf13 and friends in Go.Complete documentation is available at `,Run: func(cmd *cobra.Command, args []string) {// Do Stuff Herefmt.Printf("HAHAHA\n")},
}func Execute() {if err := rootCmd.Execute(); err != nil {fmt.Fprintln(os.Stderr, err)os.Exit(1)}
}
Create my main.go

main.go中代码如下,作用是执行根命令

package mainimport ("github/github-user/niceapp/cmd"
)func main() {cmd.Execute()
}

编译并执行niceapp

Create additional commands

为了给根命令加上子命令,创建一个新的文件cmd/version.go

package cmdimport ("fmt""github/spf13/cobra"
)func init() {rootCmd.AddCommand(versionCmd)
}var versionCmd = &cobra.Command{Use:   "version",Short: "Print the version number of Hugo",Long:  `All software has versions. This is Hugo's`,Run: func(cmd *cobra.Command, args []string) {fmt.Println("Hugo Static Site Generator v0.9 -- HEAD")},
}

重新编译并执行niceapp,在niceapp后加上子命令version

Assign flags to a command

修改root.go,给命令增加标志

rootCmd.PersistentFlags().StringVarP(&author, "author", "a", "YOUR NAME", "Author name for copyright attribution")

此时再次编译执行,即可在niceapp后添加标志a,并且可以传入a后跟着的参数

二、模仿 cobra.Command 编写一个 myCobra 库
任务要求

模仿 cobra 库的 command.go 重写一个 Command.go

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

新建一个myCobra的包,包内新建一个command.go的文件,查看cobra/command.go的源代码,
发现其首先定义了一个结构体Command,模仿cobra/command.go,定义自己的Command如下:

type Command struct {Use string        // Use is the one-line usage message.Short string      // Short is the short description shown in the 'help' output.Long string       // Long is the long message shown in the 'help <this-command>' outputmands []*Command  // commands is the list of commands supported by this program.parent *Command  // parent is a parent command for this command.Run func(cmd *Command, args []string) // Run: Typically the actual work function. Most commands will only implement this.args []string  // args is actual args parsed from flags.pflags *flag.FlagSet  // pflags contains persistent flags.
}

通过步骤一的简单练习,确定编写的Command.go主要包含的功能有:

  • 打印帮助信息
  • 支持子命令的创建和执行
  • 支持命令带选项
    有了这个目标,接下来要做的就是去实现它。
一、关键函数
AddCommand

为了支持子命令的创建,无疑要实现AddCommand()函数,AddCommand()可以接收任意个Command类型的参数,当发现接收的子命令与自身相同时,应该返回一条错误信息,即不能将自身作为自身的子命令。否则,将子命令的父命令标志为自身,并将子命令添加。

func (c *Command) AddCommand(cmds ...*Command){for i, x := range cmds {if cmds[i] == c {panic("Command can't be a child of itself")}cmds[i].parent = ccmands = append(cmands, x)}
}
PersistentFlags

获取命令后跟着的选项

func (c *Command) PersistentFlags() *flag.FlagSet {if c.pflags == nil {c.pflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)}return c.pflags
}
execute

执行命令,若当前命令后跟着子命令,则不执行此命令,转而进入子命令,若当前命令后已经没有子命令,则执行当前命令。值得一提的是,若当前命令后既跟着参数,又带着子命令。则会执行当前命令,
例如:执行

niceapp -a hello version

niceapp version

前者会执行根命令,后者会执行version命令。
具体实现思路是,当前命令发现参数中包含自身的子命令时,会执行该子命令的execute()函数,直到当前命令发现自身的参数中不包含有子命令时,才会执行当前命令。通过PersistentFlags().Parse(c.args)来解析命令中的参数。

func (c *Command) execute() (err error) {var e errorif c == nil {return fmt.Errorf("Called Execute() on a nil Command")}if c.args == nil && len(os.Args)>1 && c.parent == nil{c.args = os.Args[1:]}c.PersistentFlags().Parse(c.args)for i, x := range cmands{if len(c.args) == 0 {break}if c.args[0] == "-h" || c.args[0] == "-help"{c.printhelp()return e;}if x.Use == c.args[0]{if len(c.args) > 1{x.args = c.args[1:]}x.execute()break}if i == len(cmands)-1{c.Run(c,c.args)}}if len(c.args) == 0 || len(cmands) == 0{if len(c.args) > 0{if c.args[0] == "-h" || c.args[0] == "-help"{c.printhelp()return e;} }c.Run(c,c.args)}return e
}
printhelp

打印帮助信息,主要是模仿Cobra包中command的帮助信息格式。

func (c *Command) printhelp(){fmt.Println(c.Long)fmt.Println()fmt.Printf("Usage:\n  ")var use []stringfor p := c; p != nil; p = p.Parent() {use = append(use, p.Use)}for i:=len(use)-1; i >= 0; i--{fmt.Printf("%s ",use[i])}fmt.Printf("[flags]\n  ")if len(cmands) > 0{for i:=len(use)-1; i >= 0; i--{fmt.Printf("%s ",use[i])}fmt.Printf("[command]\n\nAvailable Commands:\n")}if c == c.Root(){fmt.Printf("  help        Help about any command\n")}for _,x := range cmands{fmt.Printf("  %-12s%s\n",x.Use,x.Short)}fmt.Printf("\nFlags:\n")c.PersistentFlags().VisitAll(func (flag *flag.Flag) {fmt.Printf("  -%1s, --%-6s %-12s%s (default \"%s\")\n", flag.Shorthand, flag.Name,  flag.Value.Type(), flag.Usage, flag.DefValue)})fmt.Printf("  -%1s, --%-19s%s%s\n", "h", "help", "help for ", c.Use)if len(cmands) > 0 {fmt.Printf("\nUse \"%s [command] --help\" for more information about a command.\n",c.Use)}
}
二、功能测试

首先安装包

go install github/github-user/myCobra

然后修改步骤一中root.go和version.go的import文件,改成自己的。

还是先执行相同的命令

修改root.go,使得命令后接参数-a,再次执行


可以看到,和练习一时得到相同的输出,试着在子命令后再加一个子命令

package cmdimport ("fmt"//"github/spf13/cobra"cobra "github/github-user/myCobra"
)func init() {versionCmd.AddCommand(verCmd)
}var versionCmd = &cobra.Command{Use:   "version",Short: "Print the version number of Hugo",Long:  `All software has versions. This is Hugo's`,Run: func(cmd *cobra.Command, args []string) {fmt.Println("Hugo Static Site Generator v0.9 -- HEAD")},
}var verCmd = &cobra.Command{Use:   "ver",Short: "Print the ver",Long:  `All software has v`,Run: func(cmd *cobra.Command, args []string) {fmt.Println("helloworld")},
}


得到子命令的输出helloworld。
试着在不同级别的命令下打印帮助信息:

kangze@ubuntu:~/gopath/src/github/github-user/niceapp$ niceapp -h
Usage of hugo:-a, --author string   Author name for copyright attribution (default "YOUR NAME")
A Fast and Flexible Static Site Generator built withlove by spf13 and friends in Go.Complete documentation is available at :hugo [flags]hugo [command]Available Commands:help        Help about any commandversion     Print the version number of Hugover         Print the verFlags:-a, --author string      Author name for copyright attribution (default "YOUR NAME")-h, --help               help for hugoUse "hugo [command] --help" for more information about a command.
kangze@ubuntu:~/gopath/src/github/github-user/niceapp$ niceapp version -h
Usage of hugo:-a, --author string   Author name for copyright attribution (default "YOUR NAME")
Usage of version:
All software has versions. This is Hugo'sUsage:hugo version [flags]hugo version [command]Available Commands:ver         Print the verFlags:-h, --help               help for versionUse "version [command] --help" for more information about a command.
kangze@ubuntu:~/gopath/src/github/github-user/niceapp$ niceapp version ver -h
Usage of hugo:-a, --author string   Author name for copyright attribution (default "YOUR NAME")
Usage of version:
Usage of ver:
All software has vUsage:hugo version ver [flags]Flags:-h, --help               help for ver

可以看到在不同级别的命令下输出的帮助信息是不同的。

三、单元测试

主要对execute函数和Addcommand函数进行测试

package myCobraimport ("testing""fmt""os/exec"
)var rootCmd = &Command{Use:   "hugo",Short: "Hugo is a very fast static site generator",Long: `A Fast and Flexible Static Site Generator built withlove by spf13 and friends in Go.Complete documentation is available at `,Run: func(cmd *Command, args []string) {fmt.Printf("HAHAHA\n")},
}
var versionCmd = &Command{Use:   "version",Short: "Print the version number of Hugo",Long:  `All software has versions. This is Hugo's`,Run: func(cmd *Command, args []string) {fmt.Println("Hugo Static Site Generator v0.9 -- HEAD")},
}
var author stringfunc TestAddCommand(t *testing.T) {for _, x := range  rootCmdmands {if x == versionCmd {t.Errorf("expected: nil got: %v\n",rootCmdmands)}}rootCmd.AddCommand(versionCmd)for _, x := range rootCmdmands {if x == versionCmd {return}}t.Errorf("expected: %v got: %v\n", versionCmd,rootCmdmands)
}func TestExecute(t *testing.T){stdout, err := exec.Command("bash", "-c", "niceapp -a xkz").Output()str := string(stdout)str2 := "xkzHAHAHA"for i:=0;i<len(str2);i++{if str[i] != str2[i]{t.Errorf("expected: %s but got %s\n",str2,str)}}if err != nil {t.Error(err)}stdout, err = exec.Command("bash", "-c", "niceapp version ver").Output()str = string(stdout)str2 = "helloworld"for i:=0;i<len(str2);i++{if str[i] != str2[i]{t.Errorf("expected: %s but got %s\n",str2,str)break}}if err != nil {t.Error(err)}
}

当执行命令得到的输出与期望输出不一致时将会输出错误信息,并且若当增加子命令增加错误时也会输出相应信息。

gitee地址

myCobra

更多推荐

golang程序包开发,复杂命令行支持

本文发布于:2024-03-09 03:35:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1723644.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:命令行   程序包   golang

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!