golang配置使用代理拉取依赖库,Swagger 在线解析API文档,Go参数解析工具 pflag、cobra、viper 在项目中的高级用法
golang配置使用代理拉取依赖库,Swagger 在线解析API文档,Go参数解析工具 pflag、cobra、viper 在项目中的高级用法。
1、查看go 的环境变量 在cmd中 输入
go env
设置GOPROXY代理:
go env -w GOPROXY=https://goproxy.cn,direct
设置GOPRIVATE来跳过私有库,比如常用的Gitlab或Gitee,中间使用逗号分隔:
go env -w GOPRIVATE=*.gitlab.com,*.gitee.com
如果在运行go mod vendor时,提示Get https://sum.golang.org/lookup/xxxxxx: dial tcp 216.58.200.49:443: i/o timeout,则是因为Go 1.13设置了默认的GOSUMDB=sum.golang.org,这个网站是被墙了的,用于验证包的有效性,可以通过如下命令关闭:
go env -w GOSUMDB=off
可以设置 GOSUMDB=“sum.golang.google.cn”, 这个是专门为国内提供的sum 验证服务。
go env -w GOSUMDB="sum.golang.google.cn"
通常报错:
http: server gave HTTP response to HTTPS client timeout
解决指令:(会话临时性),长久的可以在配置文件中配置
go env -w GOPROXY=https://goproxy.cn,direct
长久的,在~/.bashrc文件中添加:
export GOPROXY=https://goproxy.cn,direct
在1.3版本后,golang推出了go module 机制,同时发布了一个叫做 Module proxy protocol 的协议,通过这个机制,我们可以搭建go模块代理,最后通过GOPROXY变量以指引go命令在后续抓取模块的途径
但是GOPROXY拥有一个默认:https://proxy.golang.org,direct,而且很可惜 proxy.golang.org 在中国无法访问。这也是我们依赖下载时一直timeout的原因。
这里只需要我们将该变量设置为
GOPROXY=https://goproxy.cn,direct
在命令行输入
go env -w GOPROXY=https://goproxy.cn,direct
,即可正常拉取依赖
mac 电脑安装 brew 工具:
test swagger json online
您可以使用Swagger Editor或Swagger UI在线测试Swagger JSON。
Swagger Editor 是一个在线编辑器,可以让您输入或上传Swagger JSON文件,并提供验证、编辑和预览功能。您可以在 editor.swagger.io/ 上访问Swagger Editor。
Swagger UI是一个交互式的API文档生成工具,它可以根据Swagger JSON文件动态地生成API文档,以便用户更好地理解API。您可以在 petstore.swagger.io/ 上访问Swagger UI,并使用提供的演示API测试Swagger JSON。
在使用这些工具之前,您需要先生成Swagger JSON文件。您可以使用Swagger框架来定义和生成Swagger JSON文件。您可以在 swagger.io/ 上找到Swagger的官方文档和教程,以便更好地理解Swagger的使用和工作原理。
Go参数解析工具–pflag
- 概述
Go标准库提供了一个解析命令行的flag库,小型项目使用flag库其实已经足够用了,但在大型项目中应用较多的是另一个开源库:pflag,也是今天学习的对象。
pflag是一个用来替代Go标准库flag包的,兼容flag库,几乎不用更改就可以替换,pflag在大型项目中应用的比较广泛,比较知名的有:K8S,Docker,ETCD等。
pflag包github地址为:https://github.com/spf13/pflag
作者spf13大牛,还有其他好几个强大的开源库,包括配置神器Viper,命令行框架cobra等.
Cobra是一个库,其提供简单的接口来创建强大现代的CLI接口,类似于git或者go工具。同时,它也是一个应用,用来生成个人应用框架,从而开发以Cobra为基础的应用。Docker源码中使用了Cobra。
概念
Cobra基于三个基本概念commands,arguments和flags。其中commands代表行为,arguments代表数值,flags代表对行为的改变。
基本模型如下:
APPNAME VERB NOUN --ADJECTIVE或者APPNAME COMMAND ARG --FLAG
例如:
# server是commands,port是flag
hugo server --port=1313
# clone是commands,URL是arguments,brae是flags
git clone URL --bare
Commands
Commands是应用的中心点,同样commands可以有子命令(children commands),其分别包含不同的行为。
Commands的结构体如下:
type Command struct {
Use string // The one-line usage message.
Short string // The short description shown in the 'help' output.
Long string // The long message shown in the 'help <this-command>' output.
Run func(cmd *Command, args []string) // Run runs the command.
}
Flags
Flags用来改变commands的行为。其完全支持POSIX命令行模式和Go的flag包。这里的flag使用的是spf13/pflag包,具体可以参考Golang之使用Flag和Pflag.
安装与导入
安装
go get -u github.com/spf13/cobra/cobra
导入
import "github.com/spf13/cobra"
Cobra文件结构
cjapp的基本结构
▾ cjapp/
▾ cmd/
add.go
your.go
commands.go
here.go
main.go
main.go
其目的很简单,就是初始化Cobra。其内容基本如下:
package main
import (
"fmt"
"os"
"{pathToYourApp}/cmd"
)
func main() {
if err := cmd.RootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
使用cobra生成器
windows系统下使用:
go get github.com/spf13/cobra/cobra
或者在文件夹github.com/spf13/cobra/cobra下使用go install在$GOPATH/bin路径下生成cobra.exe可执行命令。
cobra init
命令cobra init [yourApp]将会创建初始化应用,同时提供正确的文件结构。同时,其非常智能,你只需给它一个绝对路径,或者一个简单的路径。
cobra.exe init cjapp
<<'COMMENT'
Your Cobra application is ready at
/home/chenjian/gofile/src/cjapp.
Give it a try by going there and running `go run main.go`.
Add commands to it by running `cobra add [cmdname]`.
COMMENT
ls -Ra /home/chenjian/gofile/src/cjapp
<<'COMMENT'
/home/chenjian/gofile/src/cjapp:
. .. cmd LICENSE main.go
/home/chenjian/gofile/src/cjapp/cmd:
. .. root.go
COMMENT
cobra add
在路径C:\Users\chenjian\GoglandProjects\src\cjapp下分别执行:
cobra add serve
<<'COMMENT'
serve created at /home/chenjian/gofile/src/cjapp/cmd/serve.go
COMMENT
cobra add config
<<'COMMENT'
config created at /home/chenjian/gofile/src/cjapp/cmd/config.go
COMMENT
cobra add create -p 'configCmd'
<<'COMMENT'
create created at /home/chenjian/gofile/src/cjapp/cmd/create.go
COMMENT
ls -Ra /home/chenjian/gofile/src/cjapp
<<'COMMENT'
/home/chenjian/gofile/src/cjapp:
. .. cmd LICENSE main.go
/home/chenjian/gofile/src/cjapp/cmd:
. .. config.go create.go root.go serve.go
COMMENT
此时你可以使用:
go run main.go
<<'COMMENT'
A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.
Usage:
cjapp [command]
Available Commands:
config A brief description of your command
help Help about any command
serve A brief description of your command
Flags:
--config string config file (default is $HOME/.cjapp.yaml)
-h, --help help for cjapp
-t, --toggle Help message for toggle
Use "cjapp [command] --help" for more information about a command.
COMMENT
go run main.go config
<<'COMMENT'
config called
COMMENT
go run main.go serve
<<'COMMENT'
serve called
COMMENT
go run main.go config create
<<'COMMENT'
create called
COMMENT
cobra生成器配置
Cobra生成器通过~/.cjapp.yaml(Linux下)或者$HOME/.cjapp.yaml(windows)来生成LICENSE。
一个.cjapp.yaml格式例子如下:
author: Chen Jian <chenjian158978@gmail.com>
license: MIT
或者可以自定义LICENSE:
license:
header: This file is part of {{ .appName }}.
text: |
{{ .copyright }}
This is my license. There are many like it, but this one is mine.
My license is my best friend. It is my life. I must master it as I must
master my life.
人工构建Cobra应用
人工构建需要自己创建main.go文件和RootCmd文件。例如创建一个Cobra应用cjappmanu
RootCmd文件
路径为cjappmanu/cmd/root.go
package cmd
import (
"fmt"
"os"
"github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var RootCmd = &cobra.Command{
Use: "chenjian",
Aliases: []string{"cj", "ccccjjjj"},
Short: "call me jack",
Long: `A Fast and Flexible Static Site Generator built with
love by spf13 and friends in Go.
Complete documentation is available at https://o-my-chenjian.com`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("OK")
},
}
var cfgFile, projectBase, userLicense string
func init() {
cobra.OnInitialize(initConfig)
// 在此可以定义自己的flag或者config设置,Cobra支持持久标签(persistent flag),它对于整个应用为全局
// 在StringVarP中需要填写`shorthand`,详细见pflag文档
RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (defalut in $HOME/.cobra.yaml)")
RootCmd.PersistentFlags().StringVarP(&projectBase, "projectbase", "b", "", "base project directory eg. github.com/spf13/")
RootCmd.PersistentFlags().StringP("author", "a", "YOUR NAME", "Author name for copyright attribution")
RootCmd.PersistentFlags().StringVarP(&userLicense, "license", "l", "", "Name of license for the project (can provide `licensetext` in config)")
RootCmd.PersistentFlags().Bool("viper", true, "Use Viper for configuration")
// Cobra同样支持局部标签(local flag),并只在直接调用它时运行
RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
// 使用viper可以绑定flag
viper.BindPFlag("author", RootCmd.PersistentFlags().Lookup("author"))
viper.BindPFlag("projectbase", RootCmd.PersistentFlags().Lookup("projectbase"))
viper.BindPFlag("useViper", RootCmd.PersistentFlags().Lookup("viper"))
viper.SetDefault("author", "NAME HERE <EMAIL ADDRESS>")
viper.SetDefault("license", "apache")
}
func Execute() {
RootCmd.Execute()
}
func initConfig() {
// 勿忘读取config文件,无论是从cfgFile还是从home文件
if cfgFile != "" {
viper.SetConfigName(cfgFile)
} else {
// 找到home文件
home, err := homedir.Dir()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// 在home文件夹中搜索以“.cobra”为名称的config
viper.AddConfigPath(home)
viper.SetConfigName(".cobra")
}
// 读取符合的环境变量
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err != nil {
fmt.Println("Can not read config:", viper.ConfigFileUsed())
}
}
main.go
main.go的目的就是初始化Cobra
package main
import (
"fmt"
"os"
"cjappmanu/cmd"
)
func main() {
if err := cmd.RootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
附加命令
附加命令可以在/cmd/文件夹中写,例如一个版本信息文件,可以创建/cmd/version.go
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
func init() {
RootCmd.AddCommand(versionCmd)
}
var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the version number of ChenJian",
Long: `All software has versions. This is Hugo's`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Chen Jian Version: v1.0 -- HEAD")
},
}
同时,可以将命令添加到父项中,这个例子中RootCmd便是父项。只需要添加:
RootCmd.AddCommand(versionCmd)
处理Flags
Persistent Flags
persistent意思是说这个flag能任何命令下均可使用,适合全局flag:
RootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
Local Flags
Cobra同样支持局部标签(local flag),并只在直接调用它时运行
RootCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")
Bind flag with Config
使用viper可以绑定flag
var author string
func init() {
RootCmd.PersistentFlags().StringVar(&author, "author", "YOUR NAME", "Author name for copyright attribution")
viper.BindPFlag("author", RootCmd.PersistentFlags().Lookup("author"))
}
Positional and Custom Arguments
Positional Arguments
Leagacy arg validation有以下几类:
NoArgs: 如果包含任何位置参数,命令报错
ArbitraryArgs: 命令接受任何参数
OnlyValidArgs: 如果有位置参数不在ValidArgs中,命令报错
MinimumArgs(init): 如果参数数目少于N个后,命令行报错
MaximumArgs(init): 如果参数数目多余N个后,命令行报错
ExactArgs(init): 如果参数数目不是N个话,命令行报错
RangeArgs(min, max): 如果参数数目不在范围(min, max)中,命令行报错
Custom Arguments
var cmd = &cobra.Command{
Short: "hello",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("requires at least one arg")
}
if myapp.IsValidColor(args[0]) {
return nil
}
return fmt.Errorf("invalid color specified: %s", args[0])
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Hello, World!")
},
}
实例
将root.go修改为以下:
package cmd
import (
"fmt"
"os"
"strings"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var cfgFile string
var echoTimes int
var RootCmd = &cobra.Command{
Use: "app",
}
var cmdPrint = &cobra.Command{
Use: "print [string to print]",
Short: "Print anything to the screen",
Long: `print is for printing anything back to the screen.
For many years people have printed back to the screen.`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Print: " + strings.Join(args, " "))
},
}
var cmdEcho = &cobra.Command{
Use: "echo [string to echo]",
Short: "Echo anything to the screen",
Long: `echo is for echoing anything back.
Echo works a lot like print, except it has a child command.`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Print: " + strings.Join(args, " "))
},
}
var cmdTimes = &cobra.Command{
Use: "times [# times] [string to echo]",
Short: "Echo anything to the screen more times",
Long: `echo things multiple times back to the user by providing
a count and a string.`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
for i := 0; i < echoTimes; i++ {
fmt.Println("Echo: " + strings.Join(args, " "))
}
},
}
func init() {
cobra.OnInitialize(initConfig)
cmdTimes.Flags().IntVarP(&echoTimes, "times", "t", 1, "times to echo the input")
// 两个顶层的命令,和一个cmdEcho命令下的子命令cmdTimes
RootCmd.AddCommand(cmdPrint, cmdEcho)
cmdEcho.AddCommand(cmdTimes)
}
func Execute() {
RootCmd.Execute()
}
func initConfig() {
// 勿忘读取config文件,无论是从cfgFile还是从home文件
if cfgFile != "" {
viper.SetConfigName(cfgFile)
} else {
// 找到home文件
home, err := homedir.Dir()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// 在home文件夹中搜索以“.cobra”为名称的config
viper.AddConfigPath(home)
viper.SetConfigName(".cobra")
}
// 读取符合的环境变量
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err != nil {
fmt.Println("Can not read config:", viper.ConfigFileUsed())
}
}
操作如下:
go run main.go
<<'COMMENT'
Usage:
app [command]
Available Commands:
echo Echo anything to the screen
help Help about any command
print Print anything to the screen
version Print the version number of ChenJian
Flags:
-h, --help help for app
Use "app [command] --help" for more information about a command.
COMMENT
go run main.go echo -h
<<'COMMENT'
echo is for echoing anything back.
Echo works a lot like print, except it has a child command.
Usage:
app echo [string to echo] [flags]
app echo [command]
Available Commands:
times Echo anything to the screen more times
Flags:
-h, --help help for echo
Use "app echo [command] --help" for more information about a command.
COMMENT
go run main.go echo times -h
<<'COMMENT'
echo things multiple times back to the user by providing
a count and a string.
Usage:
app echo times [# times] [string to echo] [flags]
Flags:
-h, --help help for times
-t, --times int times to echo the input (default 1)
COMMENT
go run main.go print HERE I AM
<<'COMMENT'
Print: HERE I AM
COMMENT
go run main.go version
<<'COMMENT'
Chen Jian Version: v1.0 -- HEAD
COMMENT
go run main.go echo times WOW -t 3
<<'COMMENT'
Echo: WOW
Echo: WOW
Echo: WOW
COMMENT
自定义help和usage
help
默认的help命令如下:
func (c *Command) initHelp() {
if c.helpCommand == nil {
c.helpCommand = &Command{
Use: "help [command]",
Short: "Help about any command",
Long: `Help provides help for any command in the application.
Simply type ` + c.Name() + ` help [path to command] for full details.`,
Run: c.HelpFunc(),
}
}
c.AddCommand(c.helpCommand)
}
可以通过以下来自定义help:
command.SetHelpCommand(cmd *Command)
command.SetHelpFunc(f func(*Command, []string))
command.SetHelpTemplate(s string)
usage
默认的help命令如下:
return func(c *Command) error {
err := tmpl(c.Out(), c.UsageTemplate(), c)
return err
}
可以通过以下来自定义help:
command.SetUsageFunc(f func(*Command) error)
command.SetUsageTemplate(s string)
先执行与后执行
Run功能的执行先后顺序如下:
PersistentPreRun
PreRun
Run
PostRun
PersistentPostRun
错误处理函数
RunE功能的执行先后顺序如下:
PersistentPreRunE
PreRunE
RunE
PostRunE
PersistentPostRunE
对不明命令的建议
当遇到不明命令,会有提出一定的建,其采用最小编辑距离算法(Levenshtein distance)。例如:
hugo srever
<<'COMMENT'
Error: unknown command "srever" for "hugo"
Did you mean this?
server
Run 'hugo --help' for usage.
COMMENT
想关闭智能提示,可以:
command.DisableSuggestions = true
// 或者
command.SuggestionsMinimumDistance = 1
或者使用SuggestFor属性来自定义一些建议,例如:
kubectl remove
<<'COMMENT'
Error: unknown command "remove" for "kubectl"
Did you mean this?
delete
Run 'kubectl help' for usage.
COMMENT
- 快速使用
package main
import (
"fmt"
"github.com/spf13/pflag"
)
var stringFlag = pflag.String("stringflag", "stringflag", "string flag usage")
var stringpFlag = pflag.StringP("stringpflag", "s", "stringpflag", "stringp flag usage")
var intFlag int
var boolFlag bool
func init() {
pflag.IntVarP(&intFlag, "intflag", "i", 0, "int flag usage")
pflag.BoolVar(&boolFlag, "boolflag", false, "bool flag usage")
}
func main() {
pflag.Parse()
// flag保存在指针的
fmt.Println("stringflag = ", *stringFlag)
fmt.Println("stringpflag = ", *stringpFlag)
// flag绑定在变量
fmt.Println("intflag = ", intFlag)
fmt.Println("boolflag = ", boolFlag)
// 也可以使用Getxxx方法获取
str, _ := pflag.CommandLine.GetString("stringflag")
fmt.Println("str = ", str)
}
可以先编译程序,然后再运行,这里直接用go run
go run pflag_fast.go
stringflag = stringflag
stringpflag = stringpflag
intflag = 0
boolflag = false
str = stringflag
在没有传递flag时,输出的都是默认值。
go run pflag_fast.go --stringflag hello -s world -i 1234 --boolflag true
stringflag = hello
stringpflag = world
intflag = 1234
boolflag = true
str = hello
指定flag的值后运行,相应的值都被设置了。
还可以使用-h打印帮助信息
go run pflag_fast.go -h
Usage of /tmp/go-build2029445686/b001/exe/pflag_fast:
--boolflag bool flag usage
-i, --intflag int int flag usage
--stringflag string string flag usage (default "stringflag")
-s, --stringpflag string stringp flag usage (default "stringpflag")
pflag: help requested
exit status 2
小结
使用pflag定义命令行参数有好几种方式,长选项/短选项/保存在指针/绑定到变量…
pflag.Parse()方法必须放在所有flag都定义后调用,否则flag就无法解析了。
3. PFlag包结构
从上面的快速使用例子里,使用Getxxx方法来获取flag的值:str, _ := pflag.CommandLine.GetString(“stringflag”)
这里调用了pflag包的CommandLine,查看源码CommandLine的定义如下:
// CommandLine is the default set of command-line flags, parsed from os.Args.
var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
实质上CommandLine是由NewFlagSet()创建的一个全局的FlagSet,再看看FlagSet的定义:
// A FlagSet represents a set of defined flags.
type FlagSet struct {
// Usage is the function called when an error occurs while parsing flags.
// The field is a function (not a method) that may be changed to point to
// a custom error handler.
Usage func()
// SortFlags is used to indicate, if user wants to have sorted flags in
// help/usage messages.
SortFlags bool
// ParseErrorsWhitelist is used to configure a whitelist of errors
ParseErrorsWhitelist ParseErrorsWhitelist
name string
parsed bool
actual map[NormalizedName]*Flag
orderedActual []*Flag
sortedActual []*Flag
formal map[NormalizedName]*Flag
orderedFormal []*Flag
sortedFormal []*Flag
shorthands map[byte]*Flag
args []string // arguments after flags
argsLenAtDash int // len(args) when a '--' was located when parsing, or -1 if no --
errorHandling ErrorHandling
output io.Writer // nil means stderr; use out() accessor
interspersed bool // allow interspersed option/non-option args
normalizeNameFunc func(f *FlagSet, name string) NormalizedName
addedGoFlagSets []*goflag.FlagSet
}
FlagSet是一个Flag的集合,几乎所有的pflag操作都是借助FlagSet的方法来实现的,默认定义了一个全局的FlagSet:CommandLine,我们拿pflag.IntVarP()方法看看定义:
// IntVarP is like IntVar, but accepts a shorthand letter that can be used after a single dash.
func IntVarP(p *int, name, shorthand string, value int, usage string) {
CommandLine.VarP(newIntValue(value, p), name, shorthand, usage)
}
// VarPF is like VarP, but returns the flag created
func (f *FlagSet) VarPF(value Value, name, shorthand, usage string) *Flag {
// Remember the default value as a string; it won't change.
flag := &Flag{
Name: name,
Shorthand: shorthand,
Usage: usage,
Value: value,
DefValue: value.String(),
}
f.AddFlag(flag)
return flag
}
// VarP is like Var, but accepts a shorthand letter that can be used after a single dash.
func (f *FlagSet) VarP(value Value, name, shorthand, usage string) {
f.VarPF(value, name, shorthand, usage)
}
其内部调用的就是CommandLine.一般使用全局的FlagSet会更加方便。
我们也可以定义自己的FlagSet:调用NewFlagSet()方法:
var output string
flagset := pflag.NewFlagSet("myflag", pflag.ContinueOnError)
flagset.StringVar(&output, "output", "txt", "Output File Format")
flagset.Parse(os.Args[1:]) // 调用的是自己创建的FlagSet的parse方法!
fmt.Println("output = ", output)
接下来看看源码里的Flag相关定义:
在pflag包里,一个命令行参数会解析为一个Flag类型的变量,Flag定义如下:
// A Flag represents the state of a flag.
type Flag struct {
Name string // name as it appears on command line (flag长选项的名称)
Shorthand string // one-letter abbreviated flag (flag短选项的名称,一个字母)
Usage string // help message (flag的使用帮助信息)
Value Value // value as set (flag的值)
DefValue string // default value (as text); for usage message (flag的默认值)
Changed bool // If the user set the value (or if left to default) (记录flag的值是否被设置过)
NoOptDefVal string // default value (as text); if the flag is on the command line without any options (当flag出现在命令行,但是没有指定选项值时的默认值)
Deprecated string // If this flag is deprecated, this string is the new or now thing to use (如果该flag被废弃,显示的使用帮助信息)
Hidden bool // used by cobra.Command to allow flags to be hidden from help/usage text (为true,则在help输出信息中隐藏这个flag)
ShorthandDeprecated string // If the shorthand of this flag is deprecated, this string is the new or now thing to use(如果该flag的短选项被废弃,显示的使用帮助信息)
Annotations map[string][]string // used by cobra.Command bash autocomple code (注解,用于cobra的bash自动补全)
}
Flag的值是一个Value类型的接口,定义如下:
// Value is the interface to the dynamic value stored in a flag.
// (The default value is represented as a string.)
type Value interface {
String() string
Set(string) error
Type() string
}
也就是说只要实现了Value接口的结构,就是一个新的类型值,我们拿源码里的String类型看看:
func (s *stringValue) Set(val string) error {
*s = stringValue(val)
return nil
}
func (s *stringValue) Type() string {
return "string"
}
func (s *stringValue) String() string { return string(*s) }
这里简单的自定义一个新类型:
type Person struct {
Name string
Age int
}
func newPersonValue(val Person, p *Person) *Person {
*p = val
return (*Person)(p)
}
func (p *Person) Set(val string) error {
strs := strings.Split(val, "@")
p.Name = strs[0]
p.Age, _ = strconv.Atoi(strs[1])
return nil
}
func (p *Person) Type() string {
return "Person"
}
func (p *Person) String() string {
return fmt.Sprintf("%s@%d", p.Name, p.Age)
}
func PersonVarP(p *Person, name, shorthand string, value Person, usage string) {
pflag.CommandLine.VarP(newPersonValue(value, p), name, shorthand, usage)
}
var personFlag Person
func init() {
PersonVarP(&personFlag, "person", "p", Person{Name: "abc", Age: 12}, "input person msg")
}
func main() {
pflag.Parse()
fmt.Printf("person = %+v\n", personFlag)
}
测试如下:
go run pflag_fast.go
person = {Name:abc Age:12}
lucas@Z-NB-0406:~/workspace/test/pflagtest$ go run pflag_fast.go -p zhangsan@18
person = {Name:zhangsan Age:18}
lucas@Z-NB-0406:~/workspace/test/pflagtest$ go run pflag_fast.go --person lisi@27
person = {Name:lisi Age:27}
lucas@Z-NB-0406:~/workspace/test/pflagtest$ go run pflag_fast.go -h
Usage of /tmp/go-build1927169625/b001/exe/pflag_fast:
-p, --person Person input person msg (default abc@12)
pflag: help requested
exit status 2
- 使用方法
多种命令行参数定义方式
定义方式按flag值存储位置分为两种:保存在指针或绑定到变量
flag值保存在指针
支持长选项,默认值和使用说明文本
比如:
var name = flag.String("name", "lucas", "Input your name")
func main() {
flag.Parse()
fmt.Println("name = ", *name)
}
支持长选项,短选项,默认值和使用说明文本
比如:
var name = flag.StringP("name", "n", "lucas", "Input your name")
func main() {
flag.Parse()
fmt.Println("name = ", *name)
}
flag值绑定到变量
支持长选项,默认值和使用说明文本
比如:
var name string
func main() {
flag.StringVar(&name, "name", "lucas", "Input your name")
flag.Parse()
fmt.Println("name = ", name)
}
支持长选项,短选项,默认值和使用说明文本
比如:
var name string
func main() {
flag.StringVarP(&name, "name", "n", "lucas", "Input your name")
flag.Parse()
fmt.Println("name = ", name)
}
从上面的函数命名中可以总结出以下规律:
函数名带P的说明是支持短选项的
函数名带Var的说明是将flag绑定到变量,否则则是存储在指针中
定义flag使用flag.String(),Bool(),Int()等,后缀可以带Var,P等
所有flag定义完成后,需要调用flag.Parse()去解析命令行参数
使用Get获取参数的值
可以使用Get的方法来获取flag的值,比如GetString(),GetInt(),其中表示pflag支持的所有类型。
需要注意的是,要获取的flag必须存在且类型必须和一致,比如获取上述的name,就可以使用GetString(“name”)来获取
注意:
这里调用这些Get方法都是FlagSet这个结构的方法集里的,以GetString为例,它的方法定义是这样的:
func (f *FlagSet) GetString(name string) (string, error)
而我们使用全局变量CommandLine调用时候需要这样使用:name, err := flag.CommandLine.GetString(“name”)
获取参数
参数在标签flag之后,在调用pflag.Parse()之后,可以调用pflag提供的几个方法用来获取非flag的参数:
// Arg returns the i'th command-line argument. Arg(0) is the first remaining argument
// after flags have been processed.
func Arg(i int) string
// Args returns the non-flag command-line arguments.
func Args() []string
// NArg is the number of arguments remaining after flags have been processed.
func NArg() int
Arg(i int): 返回第i个非flag参数
Args(): 返回所有非flag的参数
NArg(): 返回非flag的参数个数
使用示例如下:
var stringFlag = pflag.String("stringflag", "stringflag", "string flag usage")
var stringpFlag = pflag.StringP("stringpflag", "s", "stringpflag", "stringp flag usage")
var intFlag int
var boolFlag bool
func init() {
pflag.IntVarP(&intFlag, "intflag", "i", 0, "int flag usage")
pflag.BoolVar(&boolFlag, "boolflag", false, "bool flag usage")
}
func main() {
pflag.Parse()
fmt.Printf("arg个数是: %v\n", pflag.NArg())
fmt.Printf("arg: %v\n", pflag.Args())
fmt.Printf("第1个arg是:%v\n", pflag.Arg(0))
}
执行如下:
go run pflag_fast.go --stringflag Hello arg0 arg1 arg2
arg个数是: 3
arg: [arg0 arg1 arg2]
第1个arg是:arg0
没有给flag设置选项默认值(Setting no option default values for flags)
当你创建了一个flag后,可以给这个flag设置pflag.NoOptDefVal.
如果一个flag具有NoOptDefVal,并且该flag在命令行上没有传递这个flag的值,那么这个flag的值就被设置为NoOptDefVal指定的值。
示例:
var port = pflag.IntP("port", "p", 6379, "redis port")
func main() {
pflag.Lookup("port").NoOptDefVal = "8080"
pflag.Parse()
fmt.Printf("redis port = %v\n", *port)
}
执行如下:
go run pflag_fast.go
redis port = 6379
go run pflag_fast.go --port 8888
redis port = 8080
go run pflag_fast.go --port
redis port = 8080
go run pflag_fast.go --port=8888
redis port = 8888
总结结果如下表:
注意:
这里需要注意一点,就是指定了NoOptDefVal后,在命令行传递flag时,不能再用诸如–flag xxx,要用–flag=xxx,因为–flag xxx会被认为是没有设置值,从而使用了NoOptDefVal的值,xxx则会被认为是arg
命令行flag格式
pflag包的-与–是不同的,-表示短选项,–表示长选项(标准库flag包-和–作用是一样的)。
pflag包支持以下几种格式:
--flag // 支持布尔类型flag,或者设置了NoOptDefVal的flag
--flag x // 只支持没有设置NoOptDefVal的flag(这个上面的"注意"也有提到原因)
--flag=x
注意:pflag遇到–后会停止解析,这一点不同于flag包
改变或标准化flag名
pflag允许在构建代码或者使用命令行时标准化flag名,比如我们创建了flag为my-flag,但是用户传递时候写错成my.flag或者my_flag,也可以正常识别:
func wordSepNormailzeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
from := []string{"-", "_"}
to := "."
for _, sep := range from {
name = strings.Replace(name, sep, to, -1)
}
return pflag.NormalizedName(name)
}
var myFlag = pflag.String("my-flag", "myflag", "myflag test")
func main() {
pflag.CommandLine.SetNormalizeFunc(wordSepNormailzeFunc)
pflag.Parse()
fmt.Println("myFlag = ", *myFlag)
}
执行如下:
go run pflag_fast.go --my-flag=hello
myFlag = hello
go run pflag_fast.go --my_flag hello
myFlag = hello
go run pflag_fast.go --my.flag hello
myFlag = hello
go run pflag_fast.go --my+flag hello
unknown flag: --my+flag
Usage of /tmp/go-build951017333/b001/exe/pflag_fast:
--my.flag string myflag test (default "myflag")
-p, --port int redis port (default 6379)
--stringflag string string flag usage (default "stringflag")
unknown flag: --my+flag
exit status 2
弃用flag或flag缩写
pflag支持设置flag或其简写为弃用的,弃用的flag在帮助文本中会被隐藏,并且在使用弃用的flag时会打印提示信息。
示例如下:
var stringFlag = pflag.String("stringflag", "stringflag", "string flag usage")
var port = pflag.IntP("port", "p", 6379, "redis port")
var intFlag = pflag.IntP("intflag", "i", 0, "int flag usage")
func main() {
pflag.CommandLine.MarkDeprecated("port", "please use --new-port instead")
// 只弃用缩写,保留长选项
pflag.CommandLine.MarkShorthandDeprecated("intflag", "please use --intflag only")
pflag.Parse()
}
执行如下:
go run pflag_fast.go --port 1234
Flag --port has been deprecated, please use --new-port instead
go run pflag_fast.go --help
Usage of /tmp/go-build2446988640/b001/exe/pflag_fast:
--stringflag string string flag usage (default "stringflag")
pflag: help requested
exit status 2
go run pflag_fast.go -i 12
Flag shorthand -i has been deprecated, please use --intflag only
go run pflag_fast.go --intflag 12
注意:usage文本是必需的,并且不应该为空
隐藏flag
pflag包支持将flag标记为隐藏的,这样子在程序内部这个flag仍是正常运行的,但是不会显示在帮助文本中。
var stringFlag = pflag.String("stringflag", "stringflag", "string flag usage")
var port = pflag.IntP("port", "p", 6379, "redis port")
var intFlag = pflag.IntP("intflag", "i", 0, "int flag usage")
func main() {
pflag.CommandLine.MarkHidden("port")
pflag.Parse()
}
执行help结果:
go run pflag_fast.go --help
Usage of /tmp/go-build739458557/b001/exe/pflag_fast:
-i, --intflag int int flag usage
--stringflag string string flag usage (default "stringflag")
pflag: help requested
exit status 2
禁用flag排序
pflag支持在显示帮助文本时禁用flag排序,比如:
pflag.BoolP("verbose", "v", false, "verbose output")
pflag.String("coolflag", "yeaah", "it's really cool flag")
pflag.Int("usefulflag", 777, "sometimes it's very useful")
pflag.CommandLine.SortFlags = false
pflag.PrintDefaults()
输出如下(按定义的顺序输出):
-v, --verbose verbose output
--coolflag string it's really cool flag (default "yeaah")
--usefulflag int sometimes it's very useful (default 777)
如果设置pflag.CommandLine.SortFlags = true,输出如下:
--coolflag string it's really cool flag (default "yeaah")
--usefulflag int sometimes it's very useful (default 777)
-v, --verbose verbose output
支持Go的flag包
import (
goflag "flag"
flag "github.com/spf13/pflag"
)
var ip *int = flag.Int("flagname", 1234, "help message for flagname")
func main() {
flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
flag.Parse()
}
GoLang Cobra 命令行解析组件
Cobra 命令行
特性
简单的基于子命令的 CLIs:app server、app fetch 等;
完全兼容 POSIX(可移植操作系统接口) 的标志(包括短版和长版)
嵌套子命令
全局、局部和级联的标志
使用 cobra init appname 和 cobra add cmdname 轻松生成应用程序和命令
智能提示(app srver …did you mean app server)
自动生成命令和标志的帮助
自动识别 -h、–help 等帮助标识
自动为你的应用程序生成的 bash 自动完成
自动为你的应用程序生成 man 手册
命令别名,以便你可以更改内容而不会破坏它们
定义自己的帮助,用法等的灵活性。
可选与 viper 紧密集成,可用于 12factor 应用程序
安装
Cobra 非常易用,首先使用 go get 命令安装最新版本。此命令将安装 cobra generator 的可执行文件及其依赖项:
$ go get -u github.com/spf13/cobra/cobra
概念
Cobra 构建在命令(commands)、参数(arguments)和 标志(flags)上。
Commands 代表动作,Args 是事物,Flags 是这些动作的修饰符。
最好的应用程序在使用时会像句子一样读起来。用户将知道如何使用该应用程序,因为他们将自然地了解如何使用它。
遵循的模式是 APPNAME VERB NOUN --ADJECTIVE。 或 APPNAME COMMAND ARG --FLAG
一些真实的例子可以更好地说明这一点。
在以下示例中,server 是命令,port 是标志:
hugo server --port=1313
在此命令中,我们告诉 Git 克隆 url 的内容:
git clone URL --bare
命令(Command)
命令是应用程序的核心。应用程序提供的每一个交互都包含在 Command 中。一个命令可以有子命令和可选的运行一个动作。
在上面的示例中,server 是命令。
cobra.Command API
标志(Flags)
一个标志是一种修饰命令行为的方式。Cobra 支持完全符合 POSIX(可移植操作系统接口) 的标志和 Go flag 包。
Cobra 命令可以定义一直保留到子命令的标志和仅可用于该命令的标志。
在上面的例子中,port 是标志。
标志的功能是 pflag 库提供的,该库是一个标准库的 fork,在维护相同接口的基础上兼容了 POSIX(可移植操作系统接口)。
入门
欢迎大家提供自己的项目组织结构,但是通常基于 Cobra 的应用程序将遵循以下组织结构
cmd 放置命令的文件夹
add.go
your.go
commands.go
here.go
main.go 应用程序入口
在 Cobra 应用程序中,通常 main.go 文件非常。它有一个目的:初始化 Cobra。
package main
import (
"{pathToYourApp}/cmd"
)
func main() {
cmd.Execute()
}
使用 Cobra 生成器
Cobra 提供了 CLI 来创建您的应用程序和添加任意你想要的命令。这是将 Cobra 集成到您的应用程序中的最简单方法。
这里 你可以查看更多关于生成器的资料。
使用 Cobra 库
要手动接入 Cobra,您需要创建一个 main.go 文件和 rootCmd 文件。您可以选择提供合适的其他命令。
创建 rootCmd
Cobra 不需要任何特殊的构造函数。只需创建您的命令。
理想情况下,将其放置在 /cmd/root.go 中:
// rootCmd 代表没有调用子命令时的基础命令
var rootCmd = &cobra.Command{
Use: "hugo",
Short: "Hugo is a very fast static site generator",
Long: `A Fast and Flexible Static Site Generator built with
love by spf13 and friends in Go.
Complete documentation is available at http://hugo.spf13.com`,
// 如果有相关的 action 要执行,请取消下面这行代码的注释
// Run: func(cmd *cobra.Command, args []string) { },
}
// Execute 将所有子命令添加到root命令并适当设置标志。
// 这由 main.main() 调用。它只需要对 rootCmd 调用一次。
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
在 init() 函数中定义标志并处理配置。例子如下:
// cmd/root.go
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/viper"
)
var cfgFile string
var projectBase string
var userLicense string
// rootCmd 代表没有调用子命令时的基础命令
var rootCmd = &cobra.Command{
Use: "hugo",
Short: "Hugo is a very fast static site generator",
Long: `A Fast and Flexible Static Site Generator built with
love by spf13 and friends in Go.
Complete documentation is available at http://hugo.spf13.com`,
// 如果有相关的 action 要执行,请取消下面这行代码的注释
// Run: func(cmd *cobra.Command, args []string) { },
}
// Execute 将所有子命令添加到root命令并适当设置标志。会被 main.main() 调用一次。
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func init() {
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra.yaml)")
rootCmd.PersistentFlags().StringVarP(&projectBase, "projectbase", "b", "", "base project directory eg. github.com/spf13/")
rootCmd.PersistentFlags().StringP("author", "a", "YOUR NAME", "Author name for copyright attribution")
rootCmd.PersistentFlags().StringVarP(&userLicense, "license", "l", "", "Name of license for the project (can provide `licensetext` in config)")
rootCmd.PersistentFlags().Bool("viper", true, "Use Viper for configuration")
viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author"))
viper.BindPFlag("projectbase", rootCmd.PersistentFlags().Lookup("projectbase"))
viper.BindPFlag("useViper", rootCmd.PersistentFlags().Lookup("viper"))
viper.SetDefault("author", "NAME HERE <EMAIL ADDRESS>")
viper.SetDefault("license", "apache")
}
func initConfig() {
// Don't forget to read config either from cfgFile or from home directory!
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
// Find home directory.
home, err := homedir.Dir()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// Search config in home directory with name ".cobra" (without extension).
viper.AddConfigPath(home)
viper.SetConfigName(".cobra")
}
if err := viper.ReadInConfig(); err != nil {
fmt.Println("Can't read config:", err)
os.Exit(1)
}
}
创建 main.go
有了根命令,你需要一个 main 函数去执行它。为了清晰起见,Execute 应该在根目录上运行,尽管可以在任何命令上调用它。
在 Cobra 应用中,main.go 是非常简单的。它只有一个作用——初始化 Cobra。
// main.go
package main
import (
"{pathToYourApp}/cmd"
)
func main() {
cmd.Execute()
}
创建额外的命令
可以定义其他命令,并且通常在 cmd/ 目录中为每个命令提供自己的文件。
如果要创建 version 命令,则可以创建 cmd/version.go 并使用以下命令进行填充:
// cmd/version.go
package cmd
import (
"fmt"
"github.com/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")
},
}
使用标志
标志提供修饰符以控制命令的操作方式。
由于标志是在不同位置定义和使用的,我们需要在外部定义一个具有正确作用域的变量,以分配要使用的标志。
var verbose bool
var source string
这里有两种不同分配标志的方法。
持久标志
标志可以是 “persistent” 的,这意味着该标志将可用于分配给它的命令以及该命令下的每个命令。对于全局标志,将标志分配为根上的持久标志。
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output")
本地标志
也可以在本地分配一个标志,该标志仅适用于该特定命令。
rootCmd.Flags().StringVarP(&source, "source", "s", "", "Source directory to read from")
父命令上的本地标志
默认情况下,Cobra 仅解析目标命令上的本地标志,而忽略父命令上的任何本地标志。通过启用 Command.TraverseChildren,Cobra 将在执行目标命令之前解析每个命令上的本地标志
command := cobra.Command{
Use: "print [OPTIONS] [COMMANDS]",
TraverseChildren: true,
}
用配置绑定标志
您还可以将标志与 viper 绑定:
var author string
func init() {
rootCmd.PersistentFlags().StringVar(&author, "author", "YOUR NAME", "Author name for copyright attribution")
viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author"))
}
在此示例中,持久标记 author 与 viper 绑定。请注意,当用户未提供 --author 标志时,变量 author 不会设置为 config 中的值。
更多信息请查看 viper。
必需标志
标志默认是可选的。如果你想在缺少标志时命令报错,请设置该标志为必需:
var region string
rootCmd.Flags().StringVarP(®ion, "region", "r", "", "AWS region (required)")
rootCmd.MarkFlagRequired("region")
位置和自定义参数
可以使用 Command 的 Args 字段指定位置参数的验证。
下面的验证符是内置的:
NoArgs - 如果有任何位置参数,该命令将报告错误。
ArbitraryArgs - 命令将接受任意参数
OnlyValidArgs - 如果 Command 的 ValidArgs 字段中不存在该位置参数,则该命令将报告错误。
MinimumNArgs(int) - 如果不存在至少 N 个位置参数,则该命令将报告错误。
MaximumNArgs(int) - 如果存在超过 N 个位置参数,则该命令将报告错误。
ExactArgs(int) - 如果不存在 N 个位置参数,则该命令将报告错误。
ExactValidArgs(int) - 如果没有确切的 N 个位置参数,或者如果 Command 的 ValidArgs 字段中不存在该位置参数,则该命令将报告并出错。
RangeArgs(min, max) - 如果 args 的数目不在期望的 args 的最小和最大数目之间,则该命令将报告并出错。
内置验证符使用实例:
var cmd = &cobra.Command{
Use: "hello",
Short: "hello",
Args: cobra.MinimumNArgs(2),
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Hello, World!")
},
}
如果只传递一个位置参数会报 Error: requires at least 2 arg(s), only received 1 的警告。
设置自定义验证器的示例:
var cmd = &cobra.Command{
Short: "hello",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("requires at least one arg")
}
if myapp.IsValidColor(args[0]) {
return nil
}
return fmt.Errorf("invalid color specified: %s", args[0])
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Hello, World!")
},
}
示例
在下面的例子中,我们定义了三个命令。两个在顶层,一个(cmdTimes)是子命令。在这种情况下,根目录不可执行,这意味着需要一个子命令。通过不为 rootCmd 提供 Run 来实现。
我们只为一个命令定义了一个标志。
关于标志的文档在 [pflag]github.com/spf13/pflag…%E3%80%82)
package main
import (
"fmt"
"strings"
"github.com/spf13/cobra"
)
func main() {
var echoTimes int
var cmdPrint = &cobra.Command{
Use: "Print [string to print]",
Short: "Print anything to the screen",
Long: `print is for printing anything back to the screen.
For many years people have printed back to the screen.`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Print: " + strings.Join(args, " "))
},
}
var cmdEcho = &cobra.Command{
Use: "echo [string to echo]",
Short: "Echo anything to the screen",
Long: `echo is for echoing anything back.
Echo works a lot like print, except it has a child command.`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Print: " + strings.Join(args, " "))
},
}
var cmdTimes = &cobra.Command{
Use: "times [# times] [string to echo]",
Short: "Echo anyting to the screen more times",
Long: `echo things multiple times back to the user y providing
a count and a string.`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
for i := 0; i < echoTimes; i++ {
fmt.Println("Echo: " + strings.Join(args, " "))
}
},
}
cmdTimes.Flags().IntVarP(&echoTimes, "times", "t", 1, "times to echo the input")
// 设置根命令
var rootCmd = &cobra.Command{Use: "app"}
rootCmd.AddCommand(cmdPrint, cmdEcho)
cmdEcho.AddCommand(cmdTimes)
// 初始化应用
rootCmd.Execute()
}
帮助命令
当你添加了子命令,Cobra 会自动添加一些帮助命令。当你执行 app help 命令时会显示帮助信息。另外,help 还支持其他命令作为输入参数。举例来说,你有一个没有额外配置的 create 命令,app help create 是有效的。每一个命令还会自动获取一个 --help 标志。
示例
以下输出由 Cobra 自动生成。 除了命令和标志定义外,什么都不需要。
help 就像其他命令一样。并没有特殊的逻辑或行为。实际上,你可以根据需要提供自己的服务。
定义你自己的 help
你可以使用下面的方法提供你自己的 Help 命令或模板。
cmd.SetHelpCommand(cmd *Command)
cmd.setHelpCommand(f func(*Command, []string))
cmd.setHelpTemplate(s string)
后两者也适用于所有子命令。
使用信息
当用户提供无效的标志或无效的命令时,Cobra 会通过向用户显示 usage 进行响应。
定义你自己的使用信息
你可以提供你自己的 usage 函数或模板。像 help 一样,函数和模板可通过公共方法重写:
cmd.SetUsageFunc(f func(*Command) error)
cmd.SetUsageTemplate(s string)
版本标志
如果给根命令设置了 Version 字段,Cobra 会添加一个顶级的 --version 标志。运行带有 –version 标志的应用程序,将使用版本模板将版本打印到 stdout。模板可以使用 cmd.SetVersionTemplate(s string) 函数自定义。
SetVersionTemplate 的使用可以参考 GitHub CLI
PreRun 和 PostRun Hooks
可以在执行命令之前和之后运行一个函数。PersistentPreRun 和 PreRun 函数将在 Run 之前执行。PersistentPostRun 和 PostRun 会在 Run 之后运行。如果子级未声明自己的 Persistent * Run 函数,则子级将继承父级的。这些函数的执行顺续如下:
PersistentPreRun
PreRun
Run
PostRun
PersistentPostRun
下面这个包含了两个命令的例子使用了这些特性。当子命令执行时,它会运行根命令的 PersistentPreRun,但是不会运行根命令的 PersistentPostRun:
package main
import (
"fmt"
"github.com/spf13/cobra"
)
func main() {
var rootCmd = &cobra.Command{
Use: "root [sub]",
Short: "My root command",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd PersistentPreRun with args: %v\n", args)
},
PreRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd PreRun with args: %v\n", args)
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd Run with args: %v\n", args)
},
PostRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd PostRun with args: %v\n", args)
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd PersistentPostRun with args: %v\n", args)
},
}
subCmd := &cobra.Command{
Use: "sub [no options!]",
Short: "My subcommand",
PreRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside subCmd PreRun with args: %v\n", args)
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside subCmd Run with args: %v\n", args)
},
PostRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside subCmd PostRun with args: %v\n", args)
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside subCmd PersistentPostRun with args: %v\n", args)
},
}
rootCmd.AddCommand(subCmd)
rootCmd.SetArgs([]string{""})
rootCmd.Execute()
fmt.Println()
rootCmd.SetArgs([]string{"sub", "arg1", "arg2"})
rootCmd.Execute()
}
输出:
Inside rootCmd PersistentPreRun with args: []
Inside rootCmd PreRun with args: []
Inside rootCmd Run with args: []
Inside rootCmd PostRun with args: []
Inside rootCmd PersistentPostRun with args: []
Inside rootCmd PersistentPreRun with args: [arg1 arg2]
Inside subCmd PreRun with args: [arg1 arg2]
Inside subCmd Run with args: [arg1 arg2]
Inside subCmd PostRun with args: [arg1 arg2]
Inside subCmd PersistentPostRun with args: [arg1 arg2]
“unknown command” 时的提示
当 “unknown command” 错误发生时,Cobra 会自动打印提示。这和 git 命令的行为一致。比如
$ hugo srever
Error: unknown command "srever" for "hugo"
Did you mean this?
server
Run 'hugo --help' for usage.
系统会根据注册的每个子命令自动生成建议,并使用萊文斯坦距離的实现。每个匹配最小距离 2(忽略大小写)的注册命令都将显示为建议。
如果需要禁用建议或在命令中调整字符串距离,请使用:
cmd.DisableSuggestions = true
或
cmd.SuggestionsMinimumDistance = 1
还可以使用 SuggestFor 属性显式为给定命令设置建议的名称。这样就可以针对不是距离很近的字符串提出建议,但是对于您的命令集和不希望使用别名的命令来说,它们都是有意义的。比如:
$ kubectl remove
Error: unknown command "remove" for "kubectl"
Did you mean this?
delete
Run 'kubectl help' for usage.
原文地址:https://blog.csdn.net/u014374009/article/details/137261323
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!