首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Go系列:cobra命令

Go系列:cobra命令

原创
作者头像
用户9805946
发布2024-11-25 21:19:38
发布2024-11-25 21:19:38
22900
代码可运行
举报
文章被收录于专栏:GoGo
运行总次数:0
代码可运行

本文总结了在使用cobra的一些经验

参数同时支持中划线和下划线

例如 --tenant_id--tenant-id

代码语言:go
复制
for _, cmd := range RootCmd.Commands() {
    cmd.Flags().SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName {
        return pflag.NormalizedName(strings.ReplaceAll(name, "_", "-"))
    })
}

通过调用rootCmd的所有命令的SetNormalizeFunc,使得所有root命令和所有子命令都同时支持中划线参数和下划线参数

同时如果要执行参数名称的大小写不敏感,也可以使用参数这个实现

自定义flag参数类型

一般来说,我们如果想实现类似python argparse的中可选变量

代码语言:python
代码运行次数:0
运行
复制
parser.add_argument('--admin_state_up', choices=['True', 'False', 'true', 'false'], help=_('admin state of agent'))

如上的python列子中,限制admin_state_up参数只能在四种值中选择,如果输入其他值,则会报错,但是在cobra中,并没有提供内置的choices功能,可以通过自定义类型来实现这个功能

代码语言:go
复制
type AdminStateUp string

var Choices = []string{"True", "true", "False", "false"}

func (f *AdminStateUp) Set(value string) error {
	for _, choice := range Choices {
		if value == choice {
			*f = AdminStateUp(choice)
		}
	}
	return fmt.Errorf("%s is ivalid, choice=%v", value, Choices)
}

func (f *AdminStateUp) String() string {
	return fmt.Sprintf("%v", *f)
}

func (f *AdminStateUp) Type() string {
	return "string"
}

使用方法

代码语言:go
复制
adminStateUp := AdminStateUp("")
RootCmd.Flags().Var(&adminStateUp, "admin-state-up", "set admin state up")

如果输入非预期的值,会报如下错误

代码语言:bash
复制
Error: invalid argument "TRue" for "--admin-state-up" flag: TRue is ivalid, choice=[True true False false]

使用自定义类型也可以非常方便的实现参数输入校验,而不用在也业务逻辑搅合在用一起

自定义命令usage模块

对于cobra子命令来说,默认的usage不仅会显示自己的参数,还会显示parent命令的参数,如果只是想显示自己的参数,那么可以通过两种方法来自定义usage的显示

方法一 通过设置usage模版

代码语言:go
复制
SetUsageTemplate

至于模版改如何编写,可以参考 https://github.com/spf13/cobra/issues/1995

方法二 使用SetUsageFunc来自定义输出

这个我们在下一后面会讲到如何通过这个方法来实现参数分组

参数分组

在python的argparse中,可以很方便的实现参数分组,如下效果所示

代码语言:bash
复制
output formatters:
  output formatter options

  -f {csv,json,table,value,yaml}, --format {csv,json,table,value,yaml}
                        the output format, defaults to table

CSV Formatter:
  --quote {all,minimal,none,nonnumeric}
                        when to include quotes, defaults to nonnumeric

那么在cobra中如何实现这样的效果呢,诀窍就是我们前面讲过的SetUsageFunc方法。我们可以在SetUsageFunc中自定义usage输出,废话不多说,完整的例子如下

代码语言:go
复制
package main

import (
	"fmt"
	"os"
	"strings"
	"unicode"

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

type NameFlagSet struct {
	name string
	desc string
	fs   *pflag.FlagSet
}

var nameFlagSets []NameFlagSet

func main() {

	RootCmd := &cobra.Command{
		Use:   "hello",
		Short: fmt.Sprintf("Commands for Hello"),
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			if cmd.Flags().Changed("version") {
				os.Exit(0)
			}

		},
		Run: func(cmd *cobra.Command, args []string) {
			cmd.Help()
		},
		SilenceUsage: true,
	}

	fs1 := pflag.NewFlagSet("output formatters", pflag.ExitOnError)
	fs1.StringP("format", "p", "json", "the output format, defaults to table")

	fs2 := pflag.NewFlagSet("cvs formatter", pflag.ExitOnError)
	fs2.String("quote", "", "when to include quotes, defaults to nonnumeric")

	RootCmd.Flags().AddFlagSet(fs1)
	RootCmd.Flags().AddFlagSet(fs2)

	nameFlagSets = append(nameFlagSets, NameFlagSet{
		name: "output formatters",
		desc: "output formatter options",
		fs:   fs1,
	})
	nameFlagSets = append(nameFlagSets, NameFlagSet{
		name: "CSV Formatter",
		fs:   fs2,
	})

	RootCmd.SetUsageFunc(func(cmd *cobra.Command) error {
		if cmd == nil {
			return fmt.Errorf("nil command")
		}

		usage := []string{fmt.Sprintf("Usage: %s", cmd.UseLine())}

		if cmd.HasAvailableSubCommands() {
			usage = append(usage, "\nCommands:")
			for _, subCommand := range cmd.Commands() {
				usage = append(usage, fmt.Sprintf("  %-10s  %s", subCommand.Name(), subCommand.Short))
			}
		}

		for _, nfs := range nameFlagSets {
			usage = append(usage, fmt.Sprintf("\n\033[4m%s:\033[0m", nfs.name))
			if nfs.desc != "" {
				usage = append(usage, fmt.Sprintf("  %s", nfs.desc))
			}
			usage = append(usage, strings.TrimRightFunc(nfs.fs.FlagUsages(), unicode.IsSpace))
		}

		usage = append(usage,
			fmt.Sprintf("\nUse '%s [command] --help' for more information about a command.\n",
				cmd.CommandPath()))

		cmd.Println(strings.Join(usage, "\n"))

		return nil
	})

	if err := RootCmd.Execute(); err != nil {
		os.Exit(1)
	}
}

输出

代码语言:bash
复制
Usage: hello [flags]

output formatters:
  output formatter options
  -p, --format string   the output format, defaults to table (default "json")

CSV Formatter:
      --quote string   when to include quotes, defaults to nonnumeric

Use 'hello [command] --help' for more information about a command.

原理:

  1. 使用FlagSet结构体,每个group下的flag属于某个FlagSet下
  2. 定义了NameFlagSets列表,记录所有的FlagSet
  3. 在SetUsageFunc中,遍历NameFlagSets,按flagset分组进行显示

StringArray和StringSlicew的区别

  • StringSlice*
    • --key one --key two --key three ==> []string{"one", "two", "three"}
    • --key one --key two,three => []string{"one", "two", "three"}
  • StringArray*
    • --key one --key two --key three ==> []string{"one", "two", "three"}
    • --key one --key two,three => []string{"one", "two,three"}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 参数同时支持中划线和下划线
  • 自定义flag参数类型
  • 自定义命令usage模块
  • 参数分组
  • StringArray和StringSlicew的区别
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档