Contents

go第三方库-Github.com.spf13.cobra

godoc

github 官网

github 官网用户指南

目录组织结构

1
2
3
4
5
6
7
▾ appName/
  ▾ cmd/
      add.go
      your.go
      commands.go
      here.go
    main.go

main.go

1
2
3
4
5
6
7
8
9
package main

import (
  "{pathToYourApp}/cmd"
)

func main() {
  cmd.Execute()
}

使用 Cobra 库

创建一个裸 main.go 文件和一个 rootCmd 文件

创建rootCmd

app/cmd/root.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
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 https://gohugo.io/documentation/`,
  Run: func(cmd *cobra.Command, args []string) {
    // Do Stuff Here
  },
}

func Execute() {
  if err := rootCmd.Execute(); err != nil {
    fmt.Fprintln(os.Stderr, err)
    os.Exit(1)
  }
}

init() 函数中另外定义标志和句柄配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package cmd

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var (
	// Used for flags.
	cfgFile     string
	userLicense string

	rootCmd = &cobra.Command{
		Use:   "cobra-cli",
		Short: "A generator for Cobra based Applications",
		Long: `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.`,
	}
)

// Execute executes the root command.
func Execute() error {
	return rootCmd.Execute()
}

func init() {
	cobra.OnInitialize(initConfig)

	rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra.yaml)")
	rootCmd.PersistentFlags().StringP("author", "a", "YOUR NAME", "author name for copyright attribution")
	rootCmd.PersistentFlags().StringVarP(&userLicense, "license", "l", "", "name of license for the project")
	rootCmd.PersistentFlags().Bool("viper", true, "use Viper for configuration")
	viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author"))
	viper.BindPFlag("useViper", rootCmd.PersistentFlags().Lookup("viper"))
	viper.SetDefault("author", "NAME HERE <EMAIL ADDRESS>")
	viper.SetDefault("license", "apache")

	rootCmd.AddCommand(addCmd)
	rootCmd.AddCommand(initCmd)
}

func initConfig() {
	if cfgFile != "" {
		// Use config file from the flag.
		viper.SetConfigFile(cfgFile)
	} else {
		// Find home directory.
		home, err := os.UserHomeDir()
		cobra.CheckErr(err)

		// Search config in home directory with name ".cobra" (without extension).
		viper.AddConfigPath(home)
		viper.SetConfigType("yaml")
		viper.SetConfigName(".cobra")
	}

	viper.AutomaticEnv()

	if err := viper.ReadInConfig(); err == nil {
		fmt.Println("Using config file:", viper.ConfigFileUsed())
	}
}

创建main.go

通常 main.go 文件非常简单。它的目的只有一个:初始化 Cobra。

1
2
3
4
5
6
7
8
9
package main

import (
  "{pathToYourApp}/cmd"
)

func main() {
  cmd.Execute()
}

创建子命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
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")
  },
}

组织子命令

通过使用 AddCommand 来实现

推荐结构

1
2
3
4
5
6
7
8
9
├── cmd
│   ├── root.go
│   └── sub1
│       ├── sub1.go
│       └── sub2
│           ├── leafA.go
│           ├── leafB.go
│           └── sub2.go
└── main.go

返回和处理错误

使用RunE

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package cmd

import (
  "fmt"

  "github.com/spf13/cobra"
)

func init() {
  rootCmd.AddCommand(tryCmd)
}

var tryCmd = &cobra.Command{
  Use:   "try",
  Short: "Try and possibly fail at something",
  RunE: func(cmd *cobra.Command, args []string) error {
    if err := someFunc(); err != nil {
	return err
    }
    return nil
  },
}

使用标志flag

为命令分配标志

1
2
var Verbose bool
var Source string

两种不同的方法来分配标志

持久标志

标志可以是“持久的”,这意味着该标志将可用于分配给它的命令以及该命令下的每个命令

对于全局标志,将标志分配为根上的持久标志

1
rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")

本地标志

在本地分配标志,该标志仅适用于该特定命令

1
localCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")

通过配置Command.TraverseChildren为true可以将标志传递给孩子

1
2
3
4
command := cobra.Command{
  Use: "print [OPTIONS] [COMMANDS]",
  TraverseChildren: true,
}

配置绑定标志

可以使用viper绑定标志

1
2
3
4
5
6
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的值

必需的标志

默认情况下,标志是可选的,通过MarkFlagRequired和MarkPersistentFlagRequired方法将flag设为必须的

1
2
3
4
5
rootCmd.Flags().StringVarP(&Region, "region", "r", "", "AWS region (required)")
rootCmd.MarkFlagRequired("region")

rootCmd.PersistentFlags().StringVarP(&Region, "region", "r", "", "AWS region (required)")
rootCmd.MarkPersistentFlagRequired("region")

标志组

必须一起提供的不同标志,用MarkFlagsRequiredTogether方法

1
2
3
rootCmd.Flags().StringVarP(&u, "username", "u", "", "Username (required if password is set)")
rootCmd.Flags().StringVarP(&pw, "password", "p", "", "Password (required if username is set)")
rootCmd.MarkFlagsRequiredTogether("username", "password")

防止一起提供不同的标志,用MarkFlagsMutuallyExclusive方法

1
2
3
rootCmd.Flags().BoolVar(&ofJson, "json", false, "Output in JSON")
rootCmd.Flags().BoolVar(&ofYaml, "yaml", false, "Output in YAML")
rootCmd.MarkFlagsMutuallyExclusive("json", "yaml")

至少存在一个标志,用MarkFlagsOneRequired方法,可以和MarkFlagsMutuallyExclusive组合实现只能使用一个标志

1
2
3
rootCmd.Flags().BoolVar(&ofJson, "json", false, "Output in JSON")
rootCmd.Flags().BoolVar(&ofYaml, "yaml", false, "Output in YAML")
rootCmd.MarkFlagsOneRequired("json", "yaml")

注意:

  • 本地和持久标志都可以使用
  • 一个标志可能出现在多个组中
  • 一个组可以包含任意数量的标志

位置和自定义参数

位置参数的验证可以使用Command的Args字段来指定。内置以下验证器:

对于参数数量

  • NoArgs- 如果有任何位置参数,则报告错误
  • ArbitraryArgs- 接受任意数量的参数
  • MinimumNArgs(int)- 如果提供的位置参数少于 N 个,则报告错误
  • MaximumNArgs(int)- 如果提供了超过 N 个位置参数,则报告错误
  • ExactArgs(int)- 如果不存在恰好 N 个位置参数,则报告错误。
  • RangeArgs(min, max)- 如果参数数量不在min和max之间,则报告错误

对于参数内容

  • OnlyValidArgs- 如果位置参数不是Command的ValidArgs字段对应的字符串切片里的值则报错
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var cmd = &cobra.Command{
    Use: "fruit",
    Short: "Choose a fruit",
    Long: "You can choose an apple, a banana, or a cherry",
    Args: cobra.OnlyValidArgs,
    ValidArgs: []string{"apple", "banana", "cherry"},
    Run: func(cmd *cobra.Command, args []string) {
        fmt.Println("Your fruit:", args[0])
    },
}

Args未定义 或 nil,则默认为ArbitraryArgs

MatchAll(pargs …PositionalArgs)还可以将现有检查与任意其他检查相结合

1
2
3
4
5
6
7
var cmd = &cobra.Command{
  Short: "hello",
  Args: cobra.MatchAll(cobra.ExactArgs(2), cobra.OnlyValidArgs),
  Run: func(cmd *cobra.Command, args []string) {
    fmt.Println("Hello, World!")
  },
}

可以设置任何满足 func(cmd *cobra.Command, args []string) error 的自定义验证器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
var cmd = &cobra.Command{
  Short: "hello",
  Args: func(cmd *cobra.Command, args []string) error {
    // Optionally run one of the validators provided by cobra
    if err := cobra.MinimumNArgs(1)(cmd, args); err != nil {
        return err
    }
    // Run the custom validation logic
    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!")
  },
}

帮助命令

自动添加,–help

帮助中的分组命令

原文

定义您自己的帮助

可以为默认命令提供您自己的帮助命令或您自己的模板

1
2
3
cmd.SetHelpCommand(cmd *Command)
cmd.SetHelpFunc(f func(*Command, []string))
cmd.SetHelpTemplate(s string)

使用信息

用户提供无效标志或无效命令时,Cobra 会通过向用户显示“用法”进行响应

定义您自己的用法

可以提供自己的使用函数或模板供Cobra使用。与帮助一样,函数和模板可以通过公共方法重写:

1
2
cmd.SetUsageFunc(f func(*Command) error)
cmd.SetUsageTemplate(s string)

版本标志

如果在 root 命令上设置了 Version 字段,Cobra 会添加一个顶级“–version”标志

使用“–version”标志运行应用程序将使用版本模板将版本打印到标准输出

可以使用 cmd.SetVersionTemplate(s string) 方法自定义模板

PreRun 和 PostRun 钩子

可以在Command的Run主函数之前或之后运行函数

如果子级不声明自己的函数,则Persistent*Run函数将由子级继承

这些函数按以下顺序运行

  1. PersistentPreRun
  2. PreRun
  3. Run
  4. PostRun
  5. PersistentPostRun

例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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)
    },
  }

  var 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()
}

出现“未知命令”时的建议

原文

为您的命令生成文档

原文

生成 shell 文件

原文

 |