前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go之现代命令行框架Cobra

Go之现代命令行框架Cobra

原创
作者头像
f1sh
发布2024-07-27 19:21:07
1360
发布2024-07-27 19:21:07

Go之现代命令行框架Cobra

老实说,今天是我第一次见到现代命令行框架这个名词,在此之前,我并不知道这个东西的作用是什么。下面一起来了解一下这个东西。

Cobra 是一个 Go 语言开发的命令行(CLI)框架,它提供了简洁、灵活且强大的方式来创建命令行程序。它包含一个用于创建命令行程序的库(Cobra 库),以及一个用于快速生成基于 Cobra 库的命令行程序工具(Cobra 命令)。Cobra 是由 Go 团队成员 spf13Hugo 项目创建的,并已被许多流行的 Go 项目所采用,如 Kubernetes、Helm、Docker (distribution)、Etcd 等。

概念

Cobra 是建立在命令、参数和标志这三个结构之上的。要使用 Cobra 编写一个命令行程序,需要明确这三个概念。

  • 命令(COMMAND):命令表示要执行的操作。
  • 参数(ARG):是命令的参数,一般用来表示操作的对象。
  • 标志(FLAG):是命令的修饰,可以调整操作的行为。

一个好的命令行程序应当类似与

代码语言:javascript
复制
 APPNAME VERB NOUN --ADJECTIVE

在这里 VERB 代表动词,NOUN 代表名词,ADJECTIVE 代表形容词。

或是

代码语言:javascript
复制
 APPNAME COMMAND ARG --FLAG

先安装一下

代码语言:javascript
复制
 go get -u github.com/spf13/cobra@latest

安装之后就可以正常的导入使用了

创建命令

这里假设创建命令行程序hugo,可以编写如下代码

代码语言:javascript
复制
 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`,
   Run: func(cmd *cobra.Command, args []string) {
     fmt.Println("run hugo...")
   },
 }
 ​
 func Execute() {
   if err := rootCmd.Execute(); err != nil {
     fmt.Println(err)
     os.Exit(1)
   }
 }
 ​

定义根命令 rootCmdrootCmd 是一个指向 cobra.Command 结构体的指针,这个结构体定义了命令行工具的一个命令。 Use: "hugo":指定了该命令的使用方法,这里是hugo,这意味着用户在命令行中输入 hugo 时会触发这个命令。 Short: "Hugo is a very fast static site generator":提供了一个简短的描述,当用户输入 hugo -hhugo --help 时会显示这段描述。 Long 字段提供了更详细的描述,包括项目的背景信息和文档链接。这段描述会在用户输入 hugo -hhugo --help 时显示 定义 Run 方法 run是一个函数,它定义了当命令被执行时要运行的代码。这个函数接收两个参数: cmd *cobra.Command:指向当前正在执行的命令。 args []string:包含命令行参数的切片。在这个例子中,Run 方法只是简单地打印了 run hugo...。 定义 Execute 函数 Execute 函数调用 rootCmd.Execute() 来启动命令行解析和执行过程。` rootCmd.Execute()方法会解析命令行输入,找到并执行相应的命令(在这个例子中是rootCmd`)。 如果 Execute 方法返回错误(表示命令执行失败),错误信息将被打印,并且程序将以状态码 1 退出。

然后写一个main文件,作为程序的启动入口

代码语言:javascript
复制
 package main
 ​
 import (
   "cobra/cmd"
 )
 ​
 func main() {
   cmd.Execute()
 }
 ​

逻辑很简单,这里只是调用了cmd.Execute()函数来执行命令。

编译并运行

代码语言:javascript
复制
 # 编译
 $ go build -o hugo
 # 执行
 $ ./hugo
 run hugo...
 ​

也可以使用-h -help来查看这个命令行程序的使用帮助

代码语言:javascript
复制
 # ./hugo -h
 A Fast and Flexible Static Site Generator built with
                 love by spf13 and friends in Go.
                 Complete documentation is available at https://gohugo.io
 ​
 Usage:
   hugo [flags]
 ​
 Flags:
   -h, --help   help for hugo
 ​

这里打印了 cobra.Command 结构体中 Long 属性的内容,如果 Long 属性不存在,则打印 Short 属性内容。

hugo 命令用法为 hugo [flags],如 hugo --help

这个命令行程序自动支持了 -h/--help 标志。

这就是使用 Cobra 编写一个命令行程序最常见的套路,这也是 Cobra 推荐写法。

现在项目结构如下

代码语言:javascript
复制
 # tree cobra
 cobra
 ├── cmd
 │   ├── go.mod
 │   ├── go.sum
 │   └── root.go
 ├── go.mod
 ├── go.sum
 ├── hugo
 └── main.go
 ​

Cobra 程序目录结构基本如此,main.go 作为命令行程序的入口,不要写过多的业务逻辑,所有命令都应该放在 cmd/ 目录下,以后不管编写多么复杂的命令行程序都可以这么来设计。

代码语言:javascript
复制
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")
  },
}

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

添加子命令

与定义 rootCmd 一样,我们可以使用 cobra.Command 定义其他命令,并通过 rootCmd.AddCommand() 方法将其添加为 rootCmd 的一个子命令。

代码语言:javascript
复制
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")
  },
}

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

然后重新编译并运行命令行程序。

代码语言:javascript
复制
#go build -o hugo
#./hugo version                       
Hugo Static Site Generator v0.9 -- HEAD

可以看到version已经添加进来了,然后再查看帮助信息

代码语言:javascript
复制
A Fast and Flexible Static Site Generator built with
                love by spf13 and friends in Go.
                Complete documentation is available at https://gohugo.io

Usage:
  hugo [flags]
  hugo [command]

Available Commands:
  completion  Generate the autocompletion script for the specified shell
  help        Help about any command
  version     Print the version number of Hugo

Flags:
  -h, --help   help for hugo

Use "hugo [command] --help" for more information about a command.

这次的帮助信息更为丰富,除了可以使用 hugo [flags] 语法,由于子命令的加入,又多了一个 hugo [command] 语法可以使用,如 hugo version

completion 可以为指定的 Shell 生成自动补全脚本,将在 Shell 补全小节进行讲解。

help用来查看帮助,同 -h/--help 类似,可以使用 hugo help command 语法查看 command 命令的帮助信息。

version为新添加的子命令。

查看子命令的帮助信息

代码语言:javascript
复制
# ./hugo help version
All software has versions. This is Hugo's

Usage:
  hugo version [flags]

Flags:
  -h, --help   help for version

命令行标志

持久标志

如果一个标志是持久的,那么这个标志将可用于它所分配的命令以及该命令下的所有子命令。

对于全局标志而言,可以定义在根命令rootcmd上面。

代码语言:javascript
复制
var Verbose bool
rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")

本地标志

标志也可以是本地的,这意味它可能只能用于指定命令

代码语言:javascript
复制
var Source string
rootCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")

父命令的本地标志

一般默认cobra只解析目标命令上的本地标志,忽略父命令上的本地标志,通过在父命令上启动Command.TraverseChildren 属性,Cobra 将在执行目标命令之前解析每个命令的本地标志。

代码语言:javascript
复制
var rootCmd = &cobra.Command{
  Use:   "hugo",
  TraverseChildren: true,
}

必选标志

默认情况下,标志是可选的,我们可以将其设置为必选,如果没有提供这个标志,就会报错。

代码语言:javascript
复制
var Region string
rootCmd.Flags().StringVarP(&Region, "region", "r", "", "AWS region (required)")
rootCmd.MarkFlagRequired("region")

定义好以上几个标志后,为了展示效果,依次对 rootCmd.Run 方法做些修改,分别打印 VerboseSourceRegion 几个变量。

代码语言:javascript
复制
var rootCmd = &cobra.Command{
  Use:   "hugo",
  Run: func(cmd *cobra.Command, args []string) {
    fmt.Println("run hugo...")
    fmt.Printf("Verbose: %v\n", Verbose)
    fmt.Printf("Source: %v\n", Source)
    fmt.Printf("Region: %v\n", Region)
  },
}

另外,为了测试启用 Command.TraverseChildren 的效果,再添加一个 print 子命令。

代码语言:javascript
复制
var printCmd = &cobra.Command{
  Use: "print [OPTIONS] [COMMANDS]",
  Run: func(cmd *cobra.Command, args []string) {
    fmt.Println("run print...")
    fmt.Printf("printFlag: %v\n", printFlag)
    fmt.Printf("Source: %v\n", Source)
  },
}

func init() {
  rootCmd.AddCommand(printCmd)

  // 本地标志
  printCmd.Flags().StringVarP(&printFlag, "flag", "f", "", "print flag for local")
}

然后编译运行

代码语言:javascript
复制
#go build -o hugo
#./hugo -h                    
A Fast and Flexible Static Site Generator built with
                love by spf13 and friends in Go.
                Complete documentation is available at https://gohugo.io
Usage:
  hugo [flags]
  hugo [command]

Available Commands:
  completion  Generate the autocompletion script for the specified shell
  help        Help about any command
  print       
  version     Print the version number of Hugo

Flags:
  -h, --help            help for hugo
  -r, --region string   AWS region (required)
  -s, --source string   Source directory to read from
  -v, --verbose         verbose output

Use "hugo [command] --help" for more information about a command.

执行hugo

现在 -r/--region 为必选标志,不传将会得到 Error: required flag(s) "region" not set 报错。

代码语言:javascript
复制
# ./hugo -r test-region
run hugo...
Verbose: false
Source: 
Region: test-region

执行print子命令

代码语言:javascript
复制
# ./hugo print -f test-flag
run print...
printFlag: test-flag
Source: 

父命令的标志 Source 内容为空。

再看一条命令

代码语言:javascript
复制
# ./hugo -s test-source print -f test-flag
run print...
printFlag: test-flag
Source: test-source

print 子命令前,我们指定了 -s test-source 标志,-s/--source 是父命令 hugo 的标志,也能够被正确解析,这就是启用 Command.TraverseChildren 的效果。 如果我们将 rootCmdTraverseChildren 属性置为 false,则会得到 Error: unknown shorthand flag: 's' in -s 报错。

使用cobra命令创建项目

cobra的目录结构,文件,模板代码都是比较固定的

这个时候脚手架工具就派上了用场,cobra提供了cobra-cli这个工具,可以通过命令的方式快速创建一个命令行项目。

安装

代码语言:javascript
复制
go install github.com/spf13/cobra-cli@latest

使用帮助

代码语言:javascript
复制
cobra-cli -h       
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:
  cobra-cli [command]

Available Commands:
  add         Add a command to a Cobra Application
  completion  Generate the autocompletion script for the specified shell
  help        Help about any command
  init        Initialize a Cobra Application

Flags:
  -a, --author string    author name for copyright attribution (default "YOUR NAME")
      --config string    config file (default is $HOME/.cobra.yaml)
  -h, --help             help for cobra-cli
  -l, --license string   name of license for the project
      --viper            use Viper for configuration

Use "cobra-cli [command] --help" for more information about a command.
初始化模块

要使用 cobra-cli 生成一个项目,首先要手动创建项目根目录并使用 go mod 命令进行初始化。

代码语言:javascript
复制
# 创建项目目录
$ mkdir cob
# 进入项目目录
$ cd cob
# 初始化模块
$ go mod init cob
初始化命令行程序

初始化好 Go 项目,我们就可以初始化命令行程序了。

代码语言:javascript
复制
# cobra-cli init
Your Cobra application is ready at
/f1sh/data/cob
root@f1sh:/f1sh/data/cob# tree .
.
├── cmd
│   └── root.go
├── go.mod
├── go.sum
├── LICENSE
└── main.go

2 directories, 5 files
root@f1sh:/f1sh/data/cob# ls
cmd  go.mod  go.sum  LICENSE  main.go
root@f1sh:/f1sh/data/cob# go run main.go
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.

初始化的时候可能会出现cobra-cli init 命令不存在报错,可以通过配置环境变量或者使用绝对路径来解决 export PATH="~/go/bin:$PATH"

使用 cobra-cli 初始化程序非常方便,只需要一个简单的 init 命令即可完成

目录结构跟我们手动编写的程序相同,只不过多了一个 LICENSE 文件,用来存放项目的开源许可证。

通过 go run main.go 执行这个命令行程序,即可打印 rootCmd.Run 的输出结果。

脚手架自动生成的main.go如下

代码语言:javascript
复制
/*
Copyright © 2024 NAME HERE <EMAIL ADDRESS>

*/
package main

import "cob/cmd"

func main() {
	cmd.Execute()
}

自动生成的root.go如下

代码语言:javascript
复制
/*
Copyright © 2024 NAME HERE <EMAIL ADDRESS>

*/
package cmd

import (
	"os"

	"github.com/spf13/cobra"
)



// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
	Use:   "cob",
	Short: "A brief description of your application",
	Long: `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.`,
	// Uncomment the following line if your bare application
	// has an action associated with it:
	// Run: func(cmd *cobra.Command, args []string) { },
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	err := rootCmd.Execute()
	if err != nil {
		os.Exit(1)
	}
}

func init() {
	// Here you will define your flags and configuration settings.
	// Cobra supports persistent flags, which, if defined here,
	// will be global for your application.

	// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cob.yaml)")

	// Cobra also supports local flags, which will only run
	// when this action is called directly.
	rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

以上两个文件跟我们手动编写的代码没什么两样,套路完全相同,唯一不同的是每个文件头部都会多出来一个 Copyright 头信息,用来标记代码的 LICENSE

可选标志

cobra-cli 提供了如下三个标志分别用来设置项目的作者、许可证类型、是否使用 Viper 管理配置。

代码语言:javascript
复制
$ cobra-cli init --author "jianghushinian" --license mit --viper
Your Cobra application is ready at
/*以上命令我们指定可选标志后对项目进行了重新初始化。
现在 LICENSE 文件内容不再为空,而是 MIT 协议。*/
代码语言:javascript
复制
The MIT License (MIT)
Copyright © 2023 jianghushinian
Permission is hereby granted...

同时 Copyright 头信息中作者信息也会被补全。

代码语言:javascript
复制
/*
Copyright © 2023 jianghushinian

...
*/
添加命令
代码语言:javascript
复制
#cobra-cli add serve
#cobra-cli add config
#cobra-cli add create -p 'configCmd' --author "jianghushinian" --license mit --viper 

使用 -p 'configCmd' 标志指定当前命令的父命令时,configCmd 必须是小驼峰命名法,因为 cobra-cliconfig 生成的命令代码自动命名为 configCmd,而不是 config_cmd 或其他形式,这符合 Go 语言变量命名规范。

可以使用如下命令执行子命令:

代码语言:javascript
复制
#go run main.go config create
create called
使用配置取代标志

如果你不想每次生成或添加命令时都指定选项参数,则可以定义 ~/.cobra.yaml 文件来保存配置信息:

代码语言:javascript
复制
author: f1sh <f1sh@123.com>
year: 2024
license: MIT
useViper: true

再次使用 init 命令初始化程序:

代码语言:javascript
复制
cobra-cli init                                                                   
Using config file: /Users/jianghushinian/.cobra.yaml

会提示使用了 ~/.cobra.yaml 配置文件。

现在 LICENSE 文件内容格式如下:

代码语言:javascript
复制
The MIT License (MIT)

Copyright © 2024 f1sh <f1sh@123.com>

...

Copyright 头信息也会包含日期、用户名、用户邮箱。

代码语言:javascript
复制
/*
Copyright © 2024 f1sh <f1sh@123.com>

...
*/

如果你不想把配置保存在 ~/.cobra.yaml 中,cobra-cli 还提供了 --config 标志来指定任意目录下的配置文件。

到这里,常用功能就总结的差不多了。cobra是一个功能强大的命令行框架,进一步提高了命令行编写程序的效率。

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Go之现代命令行框架Cobra
    • 概念
      • 创建命令
        • 编译并运行
          • 添加子命令
            • 命令行标志
              • 持久标志
              • 本地标志
              • 父命令的本地标志
              • 必选标志
            • 使用cobra命令创建项目
            相关产品与服务
            命令行工具
            腾讯云命令行工具 TCCLI 是管理腾讯云资源的统一工具。使用腾讯云命令行工具,您可以快速调用腾讯云 API 来管理您的腾讯云资源。此外,您还可以基于腾讯云的命令行工具来做自动化和脚本处理,以更多样的方式进行组合和重用。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档