跳过正文
  1. 文章/
  2. GoLang/
  3. CLI/

1、Cobra

·2431 字·5 分钟· loading · loading · ·
GoLang CLI
GradyYoung
作者
GradyYoung
CLI - 点击查看当前系列文章
§ 1、Cobra 「 当前文章 」

Cobra 基础概念
#

Cobra 是一个可以创建强大的现代 CLI 应用程序的库,它还提供了一个可以生成应用和命令文件的程序的命令行工具:cobra-cli。有许多大型项目都是用 cobra 来构建他们的应用程序,例如:kubernetes、Docker、Etcd、Rkt、Hugo 等。Cobra 具有很多特性,一些核心特性如下:

  • 可以构建基于子命令的 CLI,并支持支持嵌套子命令。例如:app serverapp fetch
  • 可以通过 cobra-cli init appname & cobra-cli add cmdname 轻松生成应用和子命令。
  • 智能化命令建议(app srver… did you mean app server?)。
  • 自动生成命令和标志的 help 文本,并能自动识别 -h--help 等标志。
  • 自动为你的应用程序生成 bashzshfishpowershell 自动补全脚本。
  • 支持命令别名、自定义帮助、自定义用法等。
  • 可以与 viperpflag 紧密集成,用于构建 12-factor 应用程序。

核心概念与架构设计
#

Cobra 建立在 commandsargumentsflags 结构之上。commands 代表命令,arguments 代表非选项参数,flags 代表选项参数(也叫标志)。一个好的应用程序应该是易懂的,用户可以清晰的知道如何去使用这个应用程序。应用程序通常遵循如下模式:APPNAME VERB NOUN --ADJECTIVE 或者 APPNAME COMMAND ARG --FLAG,例如:

git clone URL --bare # clone 是一个命令,URL 是一个非选项参数,bare 是一个选项参数

Cobra采用分层的命令树架构,核心由cobra.Command结构体组成,每个命令可以包含子命令、标志参数、执行逻辑。其核心架构图如下:

image-20250522102417262

命令结构体剖析
#

cobra.Command是Cobra的核心数据结构,关键字段包括:

type Command struct {
    Use string              // 命令使用语法(如"create user")
    Short string            // 简短描述
    Long string             // 详细描述
    Args func(args []string, cmd *Command) error // 参数验证函数
    Run func(cmd *Command, args []string)        // 命令执行函数
    Flags pflag.FlagSet     // 标志参数集合
    SubCommands []*Command  // 子命令列表
    // 其他字段:Completion、Hooks、Annotations等
}

命令树构建原则
#

  • 单一职责:每个子命令专注于单一功能(如todo add/todo list
  • 层级深度:建议不超过3层子命令,避免用户记忆负担
  • 参数作用域:标志参数可以在当前命令或继承自父命令
  • 帮助信息:自动生成的帮助基于命令树结构动态渲染

核心交互流程
#

用户输入解析流程如下:

  1. 输入解析
  2. 识别根命令
  3. 匹配子命令链
  4. 解析标志参数
  5. 验证参数合法性
  6. 执行Run函数
  7. 输出结果或错误

cobra-cli
#

Cobra 提供了一个 cobra-cli 命令,用来初始化一个应用程序并为其添加命令,方便我们开发基于 Cobra 的应用。cobra-cli 命令安装方法如下:

go install github.com/spf13/cobra-cli@latest

cobra-cli 命令提供了 4 个子命令:

  • init:初始化一个 cobra 应用程序;
  • add:给通过 cobra init 创建的应用程序添加子命令;
  • completion:为指定的 shell 生成命令自动补全脚本;
  • help:打印任意命令的帮助信息。

cobra-cli 命令还提供了一些全局的参数:

  • -a, --author:指定 Copyright 版权声明中的作者;
  • --config:指定 cobra 配置文件的路径;
  • -l, --license:指定生成的应用程序所使用的开源协议,内置的有:GPLv2, GPLv3, LGPL, AGPL, MIT, 2-Clause BSD, 3-Clause BSD
  • --viper:使用 viper 作为命令行参数解析工具,默认为 true

基本使用
#

在构建 cobra 应用时,我们可以自行组织代码目录结构,但 cobra 建议如下目录结构:

.
├── cmd
   └── root.go
   └── add.go
   └── xxx.go
├── go.mod
└── main.go

生成项目基本结构
#

# 创建项目目录
mkdir cli-demo && cd cli-demo
# 初始化go mod
go mod init ygang.top/cli-demo
# 生成 cobra 项目基本结构
cobra-cli init

生成的root.go

package cmd

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"
)

// rootCmd 表示调用时不带任何子命令的基本命令
var rootCmd = &cobra.Command{
	Use:   "cli-demo",
	Short: "您的应用程序的简要说明",
	Long: `一个跨越多行的较长描述,可能包含使用应用程序的示例和用法。例如:
Cobra是Go的一个CLI库,它为应用程序提供支持。
此应用程序是生成所需文件的工具,快速创建Cobra应用程序。`,
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println("Use 'cli-demo --help' for available commands")
	},
}

// Execute 将所有子命令添加到根命令中,并适当设置标志。
// 这是由main.main()调用的。
func Execute() {
	err := rootCmd.Execute()
	if err != nil {
		os.Exit(1)
	}
}

func init() {
  // 注册标志参数
	rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

生成的main.go

package main

import "ygang.top/cli-demo/cmd"

func main() {
	cmd.Execute()
}

添加子命令
#

package cmd

import (
	"fmt"
	"github.com/spf13/cobra"
)

var helloCmd = &cobra.Command{
	Use:   "hello <name>",
	Short: "Say Hello for <name>",
	Long:  "Say Hello for <name>",
	Args:  cobra.ExactArgs(1), // 必须有一个参数
	Run: func(cmd *cobra.Command, args []string) {
		// 解析标志
		turn, err := cmd.Flags().GetBool("turn")
		if err != nil {
			fmt.Println(err)
			return
		}
		if turn {
			fmt.Printf("%s Hello !!!\n", args[0])
		} else {
			fmt.Printf("Hello %s !!!\n", args[0])
		}
	},
}

func init() {
	rootCmd.AddCommand(helloCmd)
	// 添加命令专属标志
	helloCmd.Flags().BoolP("turn", "t", false, "can turn output")
}

常用API
#

Command
#

Args
#

在使用命令的过程中,经常会传入非选项参数,并且需要对这些非选项参数进行验证,cobra 提供了机制来对非选项参数进行验证。可以使用 CommandArgs 字段来验证非选项参数。

函数 描述
NoArgs 如果存在任何非选项参数,该命令将报错
ArbitraryArgs 该命令将接受任何非选项参数
OnlyValidArgs 如果有任何非选项参数不在 CommandValidArgs字段中,该命令将报错
MinimumNArgs(int) 如果没有至少 N 个非选项参数,该命令将报错
MaximumNArgs(int) 如果有多于 N 个非选项参数,该命令将报错
ExactArgs(int) 如果非选项参数个数不为 N,该命令将报错
ExactValidArgs(int) 如果非选项参数的个数不为 N,或者非选项参数不在 CommandValidArgs字段中,该命令将报错
RangeArgs(min, max) 如果非选项参数的个数不在 minmax之间,该命令将报错
var cmd = &cobra.Command{
  Short: "hello",
  Args: cobra.MinimumNArgs(1), // 使用内置的验证函数
  Run: func(cmd *cobra.Command, args []string) {
    fmt.Println("Hello, World!")
  },
}

自定义验证

var cmd = &cobra.Command{
  Short: "hello",
  // Args: cobra.MinimumNArgs(10), // 使用内置的验证函数
  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!")
  },
}

Help
#

在使用应用程序时,我们需要知道该应用程序的调用方法,所以需要有一个 Help 命令或者选项参数,Cobra 的强大之处也在于所有我们需要的功能 cobra 都已经帮我们实现好了。

我们也可以定义自己的 help 命令。使用如下函数,可以定义 help 命令:

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

Flag
#

Cobra支持三种标志类型:

  • Persistent Flags:对所有子命令生效(在父命令注册)
  • Local Flags:仅对当前命令生效(在子命令注册)
  • Deprecated Flags:标记为废弃的标志(支持过渡兼容)

绑定标志
#

helloCmd.Flags().BoolP("turn", "t", false, "can turn output")

还有一种绑定的方式,可以直接使用变量去接收标志的值

var b bool
helloCmd.Flags().BoolVarP(&b, "turn", "t", false, "can turn output")

标志解析
#

// 在命令Run函数中获取标志值
urgent, _ := cmd.Flags().GetBool("urgent")
configPath, _ := cmd.Flags().GetString("config")

必选标志
#

helloCmd.Flags().BoolP("turn", "t", false, "can turn output")
helloCmd.MarkFlagRequired("turn") // 设置必选
CLI - 点击查看当前系列文章
§ 1、Cobra 「 当前文章 」