y

ywanbing

V1

2022/05/06阅读:22主题:默认主题

Cobra 命令行开发

问题

在网上查找过很对关于 cobra 的开发资料,大多数都是复制官网的教程,极度缺乏参考价值;所以在开发完项目之后,准备自己来写一篇 cobra 的开发教程。

Cobra 概述

Cobra 是一个库,它提供了一个简单的界面来创建强大的现代 CLI 界面,类似于 git 和 go 工具。

眼镜蛇提供:

  • 简单的基于子命令的 CLI:app serverapp fetch等。
  • 完全符合 POSIX 的标志(包括短版和长版)
  • 嵌套子命令
  • 全局、本地和级联标志
  • 明智的建议(app srver......你的意思是app server?)
  • 命令和标志的自动帮助生成
  • -h,--help等的自动帮助标志识别
  • 为您的应用程序自动生成 shell 自动完成功能(bash、zsh、fish、powershell)
  • 为您的应用程序自动生成的手册页
  • 命令别名,这样您就可以在不破坏它们的情况下进行更改
  • 定义您自己的帮助、使用等的灵活性。
  • 可选与viper[1]无缝集成,用于 12-factor 应用程序。

安装

使用 Cobra 很容易。首先,用于go get安装最新版本的库。

go get -u github.com/spf13/cobra@latest

接下来,在您的应用程序中包含 Cobra:

import "github.com/spf13/cobra"

用法

使用 cobra 自带的 cli 工具 生成项目

安装 cli 工具

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

使用方式

  1. 创建一个新的文件夹
  2. cd 到这个文件夹里面去
  3. 运行 go mod init <MODNAME> 来创建一个go mod 项目

例如:

cd $HOME/code 
mkdir myapp
cd myapp
go mod init github.com/spf13/myapp

初始化 Cobra CLI 应用程序

从 Go 模块中运行 cobra-cli init。这将创建一个新的基本项目让您修改。

您将需要打开并编辑 cmd/root.go 并提供您自己的描述和逻辑。

例如:

cd $HOME/code/myapp
cobra-cli init
go run main.go

将命令添加到项目

初始化 cobra 应用程序后,您可以继续使用 Cobra 生成器向您的应用程序添加其他命令。执行此操作的命令是cobra-cli add.

在您的项目目录(您的 main.go 文件所在的位置)中,您将运行以下命令:

cobra-cli add serve
cobra-cli add config
cobra-cli add create -p 'configCmd'

其中还有很多标志我这里就不在细说了,需要了解的可以访问 cobra-cli[2] 自行查看。

采用手动进行项目开发

我们先来看一下 cobra 推荐的项目目录结构

▾ appName/
    ▾ cmd/
        add.go
        your.go
        commands.go
        here.go
    main.go

并且在main.go 中应该非常的简单:

package main

import (
  "{pathToYourApp}/cmd"
)

func main() {
  cmd.Execute()
}

然后需要创建一个 rootCmd 来当做 命令的入口 (在 cmd/root.go 中)

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`
,
  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() 函数中另外定义标志和句柄配置。

package cmd

import (
 "fmt"
 "os"

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

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

 rootCmd = &cobra.Command{}
)

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

接下来就是开发 配置各种 cobra cmd 的参数,在配置之前,我们先来熟悉一下 cmd 的内容。

type Command struct {
 // 使用是单行使用消息。
    // 推荐的语法如下: 
    //    [ ] 标识一个可选参数。未括在括号中的参数是必需的。 
    //    ... 表示您可以为前一个参数指定多个值。 
    //    |表示互斥信息。您可以使用分隔符左侧的参数或分隔符右侧的参数。您不能在一次使用该命令时同时使用这两个参数。
    //    { } 分隔一组互斥参数,当需要其中一个参数时。如果参数是可选的,则将它们括在方括号 ([ ]) 中。
    // 示例:add [-F file | -D dir]... [-f format] profile
 Use string

 // Aliases 是一个别名数组,可以用来代替 Use 中的第一个单词。
 Aliases []string

 // SuggestFor 是一个命令名称数组,将为其建议该命令 - 类似于别名,但只是建议。
 SuggestFor []string

 // Short 是“帮助”输出中显示的简短描述。
 Short string

 // Long 是“help <this-command>”输出中显示的长消息。
 Long string

 // Example is examples of how to use the command.
 Example string

 // ValidArgs 是 shell 完成中接受的所有有效非标志参数的列表,(提供的自动补全的参数)
 ValidArgs []string
 // ValidArgsFunction 是一个可选函数,它为 shell 完成提供有效的非标志参数。
    // 它是使用 ValidArgs 的动态版本。只有 ValidArgs 和 ValidArgsFunction 之一可用于命令。
 ValidArgsFunction func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)

 // 判断参数的格式,内置多种验证格式。
 Args PositionalArgs

 // ArgAliases 是 ValidArgs 的别名列表。这些不是在 shell 完成中向用户建议的,但如果手动输入则接受。
 ArgAliases []string

 // BashCompletionFunction 是传统 bash 自动完成生成器使用的自定义 bash 函数。
    // 为了与其他 shell 的可移植性,建议改用 ValidArgsFunction
 BashCompletionFunction string

 // Deprecated defines, if this command is deprecated and should print this string when used.
 Deprecated string

 // Annotations are key/value pairs that can be used by applications to identify or
 // group commands.
 Annotations map[string]string

 // Version defines the version for this command. If this value is non-empty and the command does not
 // define a "version" flag, a "version" boolean flag will be added to the command and, if specified,
 // will print content of the "Version" variable. A shorthand "v" flag will also be added if the
 // command does not define one.
 Version string

 // Run 函数按以下顺序执行:
 //   * PersistentPreRun()
 //   * PreRun()
 //   * Run()
 //   * PostRun()
 //   * PersistentPostRun()
 // 所有函数都获得相同的参数,即命令名称后面的参数。
 //
 // PersistentPreRun: 该命令的子级将继承并执行。
 PersistentPreRun func(cmd *Command, args []string)
 // PersistentPreRunE: 上面的返回错误形式
 PersistentPreRunE func(cmd *Command, args []string) error
 // PreRun: 此命令的子级不会继承。
 PreRun func(cmd *Command, args []string)
 // PreRunE: 上面的返回错误形式
 PreRunE func(cmd *Command, args []string) error
 // Run: 通常是实际的功函数。大多数命令只会实现这一点。
 Run func(cmd *Command, args []string)
 // RunE: 上面返回错误的形式
 RunE func(cmd *Command, args []string) error
 // PostRun: 在 run 命令之后运行。
 PostRun func(cmd *Command, args []string)
 // PostRunE: 上面返回错误的形式。
 PostRunE func(cmd *Command, args []string) error
 // PersistentPostRun: 该命令的子命令将在 PostRun 之后继承并执行。
 PersistentPostRun func(cmd *Command, args []string)
 // PersistentPostRunE: 上面返回错误的形式。
 PersistentPostRunE func(cmd *Command, args []string) error

    ...

 //FParseErrWhitelist flag parse errors to be ignored
 FParseErrWhitelist FParseErrWhitelist

 // CompletionOptions 是一组用于控制 shell 完成处理的选项 (下面给出了对应的结构体)
 CompletionOptions CompletionOptions

 ...
    
 // TraverseChildren 在执行子命令之前解析所有父项的标志。
 TraverseChildren bool

 // Hidden defines, 如果此命令被隐藏并且不应该出现在可用命令列表中。
 Hidden bool

 ...
}

// CompletionOptions are the options to control shell completion
type CompletionOptions struct {
    // DisableDefaultCmd 防止 Cobra 创建默认的“完成”命令 (禁用这么默认的 cmd 命令)
 DisableDefaultCmd bool
 // 防止 Cobra 为支持完成描述的 shell 创建“--no-descriptions”标志
 DisableNoDescFlag bool
 // DisableDescriptions 关闭支持它们的 shell 的所有完成描述
 DisableDescriptions bool
    // HiddenDefaultCmd 隐藏默认的“完成”命令 (一般情况下,采用隐藏这个 cmd )
 HiddenDefaultCmd bool
}

这里大概就看了 Cmd 的大概使用的结构体和字段,在开发的时候对应需要的东西,直接设置对应的值即可。

Short 的描述使用 shell 自动完成提示信息使用。

Long 的描述用于 -h help 打印使用。

生成自动提示命令的功能

具体的操作步骤可以看 生成外壳完成[3]

在这里需要说一句的是:使用命令生成的时候默认是打印在 控制台,需要自己重定义在指定的目录文件中去。

在根据不同的平台终端,配置好这个文件,然后就可以使用 自动提示完成的 功能了。

关注订阅号:

GolangNewbie GO菜鸟

学习更多!

参考资料

[1]

可选与viper: http://github.com/spf13/viper

[2]

cobra-cli: https://github.com/spf13/cobra-cli

[3]

生成外壳完成: https://github.com/spf13/cobra/blob/master/shell_completions.md

分类:

后端

标签:

Golang

作者介绍

y
ywanbing
V1