1. 程式人生 > >github.com/spf13/cobra go cobra包介紹

github.com/spf13/cobra go cobra包介紹

Cobra既是用於建立強大的現代CLI應用程式的庫,也是用於生成應用程式和命令檔案的程式。

許多最廣泛使用的Go專案都是使用Cobra構建的,包括:

Kubernetes
Hugo
rkt
etcd
Moby (former Docker)
Docker (distribution)
OpenShift
Delve
GopherJS
CockroachDB
Bleve
ProjectAtomic (enterprise)
GiantSwarm's swarm
Nanobox/Nanopack
rclone
nehm
Pouch

概述

Cobra是一個庫,提供了一個簡單的介面來建立類似於git&go工具的強大的現代CLI介面。

Cobra也是一個應用程式,它將生成您的應用程式支架,以快速開發基於Cobra的應用程式。

Cobra提供:

簡單易用的基於CLI:app server,app fetch等。
完全符合POSIX標準(包括including short & long versions)
巢狀的子命令
全域性,本地和級聯標誌
易產生的應用程式和命令與cobra init appname&cobra add cmdname
智慧建議
命令和標誌的自動幫助生成
自動幫助標誌識別-h,--help等等。
為您的應用程式自動生成bash自動完成
為您的應用程式自動生成的手冊頁
命令別名,以便您可以在不破壞它們的情況下進行更改
靈活定義您自己的幫助,用法等。
可選擇與viper緊密整合,適用於12-factor應用

概念

Cobra建立在命令,引數和標誌的結構上。

Commands代表動作,Args代表引數,Flags是這些動作的修飾符。

最好的應用程式在使用時會像句子一樣讀取。使用者將知道如何使用該應用程式,因為他們將原生地瞭解如何使用它。

要遵循的模式是 APPNAME VERB NOUN --ADJECTIVE. 或 APPNAME COMMAND ARG --FLAG

使用以下示例進行說明,在以下示例中,'server'是一個命令,'port'是一個標誌:

hugo server --port=1313

在這個命令中,我們告訴Git克隆url

git clone URL --bare

Commands命令

命令是應用程式的中心點。應用程式支援的每個互動都將包含在命令中。命令可以具有子命令並可選地執行動作。

Flags標誌

標誌是一種修改命令列為的方法。Cobra支援完全符合POSIX標準的標誌以及Go 標誌包。Cobra命令可以定義持久儲存到子命令和標誌的標誌,這些命令和標誌僅對該命令可用。

在上面的例子中,'port'是標誌。

標誌功能由pflag庫提供,pflag庫是標準庫的一個分支,它在新增POSIX相容性時保持相同的介面。

Installing安裝

使用cobra很容易。首先,使用go get安裝最新版本的庫。此命令將安裝cobra生成器可執行檔案以及庫及其依賴項:

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

接下來,在您的應用程式中包含Cobra:

import "github.com/spf13/cobra"

Getting Started入門

雖然歡迎您提供自己的組織,但通常基於Cobra的應用程式將遵循以下組織結構:

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

在Cobra應用程式中,通常main.go是暴露的檔案。它有一個目的:初始化Cobra

package main
import (
  "{pathToYourApp}/cmd"
)
func main() {
  cmd.Execute()
}

使用Cobra生成器

Cobra提供了自己的程式,可以建立您的應用程式並新增您想要的任何命令。這是將Cobra整合到您的應用程式中的最簡單方法。

在這裡您可以找到有關它的更多資訊。

使用Cobra Library

要手動實現Cobra,您需要建立一個暴露的main.go檔案和一個rootCmd檔案。您可以選擇根據需要提供其他命令。

建立rootCmd

cobra不需要任何特殊的建構函式。只需建立命令即可。

理想情況下,您將其放在app / 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.Println(err)
    os.Exit(1)
  }
}

您還將在init()函式中定義標誌和控制代碼配置。

例如cmd / root.go:

import (
  "fmt"
  "os"

  homedir "github.com/mitchellh/go-homedir"
  "github.com/spf13/cobra"
  "github.com/spf13/viper"
)

func init() {
  cobra.OnInitialize(initConfig)
  rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra.yaml)")
  rootCmd.PersistentFlags().StringVarP(&projectBase, "projectbase", "b", "", "base project directory eg. github.com/spf13/")
  rootCmd.PersistentFlags().StringP("author", "a", "YOUR NAME", "Author name for copyright attribution")
  rootCmd.PersistentFlags().StringVarP(&userLicense, "license", "l", "", "Name of license for the project (can provide `licensetext` in config)")
  rootCmd.PersistentFlags().Bool("viper", true, "Use Viper for configuration")
  viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author"))
  viper.BindPFlag("projectbase", rootCmd.PersistentFlags().Lookup("projectbase"))
  viper.BindPFlag("useViper", rootCmd.PersistentFlags().Lookup("viper"))
  viper.SetDefault("author", "NAME HERE <EMAIL ADDRESS>")
  viper.SetDefault("license", "apache")
}

func initConfig() {
  // Don't forget to read config either from cfgFile or from home directory!
  if cfgFile != "" {
    // Use config file from the flag.
    viper.SetConfigFile(cfgFile)
  } else {
    // Find home directory.
    home, err := homedir.Dir()
    if err != nil {
      fmt.Println(err)
      os.Exit(1)
    }

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

  if err := viper.ReadInConfig(); err != nil {
    fmt.Println("Can't read config:", err)
    os.Exit(1)
  }
}

建立你的main.go

使用root命令,您需要讓main函式執行它。為了清楚起見,應該在root上執行Execute,儘管可以在任何命令上呼叫它。

在Cobra應用程式中,通常在main.go暴露。它有一個目的,就是初始化Cobra。

package main

import (
  "fmt"
  "os"
  "{pathToYourApp}/cmd"
)

func main() {
  cmd.Execute()
}

建立其他命令

可以定義其他命令,並且通常在cmd /目錄中為每個命令提供自己的檔案。

如果要建立版本命令,可以建立cmd / version.go並使用以下內容填充它:

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")
  },
}

使用Flags

Flags提供修飾符來控制動作命令的操作方式。

為命令分配Flags

由於flag是在不同的位置定義和使用的,因此我們需要在外部定義一個具有正確範圍的變數來分配要使用的標誌。

var Verbose bool
var Source string

分配標誌有兩種不同的方法。

持久的Flags

Flags可以是“持久的”,這意味著該標誌可用於它所分配的命令以及該命令下的每個命令。對於全域性標誌,在根上分配Flag作為持久Flag

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

本地Flags

還可以在本地分配一個Flag,該Flag僅適用於該特定命令。

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

父命令上的本地Flags

預設情況下,Cobra僅解析目標命令上的本地Flag,忽略父命令上的任何本地Flag。通過啟用Command.TraverseChildrenCobra將在執行目標命令之前解析每個命令上的本地Flag

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

使用配置繫結Flags

你也可以用viper繫結你的Flags

var author string

func init() {
  rootCmd.PersistentFlags().StringVar(&author, "author", "YOUR NAME", "Author name for copyright attribution")
  viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author"))
}

在此示例中,持久Flags author與之繫結viper。 請注意author--author使用者未提供該標誌時,該變數將不會設定為config中的值。

必需的Flags

Flags預設是可選的。如果您希望命令在未設定Flags時報告錯誤,請將其標記為必需:

rootCmd.Flags().StringVarP(&Region, "region", "r", "", "AWS region (required)")
rootCmd.MarkFlagRequired("region")

位置和自定義引數

可以使用Args欄位來指定位置引數的驗證Command

以下驗證器內建:

  • NoArgs - 如果存在任何位置引數,該命令將報告錯誤。
  • ArbitraryArgs - 該命令將接受任何args。
  • OnlyValidArgs- 如果存在任何不在ValidArgs欄位中的位置引數,該命令將報告錯誤Command
  • MinimumNArgs(int) - 如果沒有至少N個位置引數,該命令將報告錯誤。
  • MaximumNArgs(int) - 如果有多於N個位置引數,該命令將報告錯誤。
  • ExactArgs(int) - 如果沒有確切的N位置引數,該命令將報告錯誤。
  • RangeArgs(min, max) - 如果args的數量不在預期args的最小和最大數量之間,則該命令將報告錯誤。

設定自定義驗證器的示例:

var cmd = &cobra.Command{
  Short: "hello",
  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!")
  },
}

示例:

在下面的示例中,我們定義了三個命令。兩個位於頂層,一個(cmdTimes)是頂級命令之一的子級。在這種情況下,root不可執行,這意味著需要子命令。這是通過不為'rootCmd'提供'Run'來實現的。

我們只為一個命令定義了一個標誌。

package main

import (
  "fmt"
  "strings"

  "github.com/spf13/cobra"
)

func main() {
  var echoTimes int

  var cmdPrint = &cobra.Command{
    Use:   "print [string to print]",
    Short: "Print anything to the screen",
    Long: `print is for printing anything back to the screen.
For many years people have printed back to the screen.`,
    Args: cobra.MinimumNArgs(1),
    Run: func(cmd *cobra.Command, args []string) {
      fmt.Println("Print: " + strings.Join(args, " "))
    },
  }

  var cmdEcho = &cobra.Command{
    Use:   "echo [string to echo]",
    Short: "Echo anything to the screen",
    Long: `echo is for echoing anything back.
Echo works a lot like print, except it has a child command.`,
    Args: cobra.MinimumNArgs(1),
    Run: func(cmd *cobra.Command, args []string) {
      fmt.Println("Print: " + strings.Join(args, " "))
    },
  }

  var cmdTimes = &cobra.Command{
    Use:   "times [# times] [string to echo]",
    Short: "Echo anything to the screen more times",
    Long: `echo things multiple times back to the user by providing
a count and a string.`,
    Args: cobra.MinimumNArgs(1),
    Run: func(cmd *cobra.Command, args []string) {
      for i := 0; i < echoTimes; i++ {
        fmt.Println("Echo: " + strings.Join(args, " "))
      }
    },
  }

  cmdTimes.Flags().IntVarP(&echoTimes, "times", "t", 1, "times to echo the input")

  var rootCmd = &cobra.Command{Use: "app"}
  rootCmd.AddCommand(cmdPrint, cmdEcho)
  cmdEcho.AddCommand(cmdTimes)
  rootCmd.Execute()
}

有關更大應用程式的更完整示例,請檢視Hugo

幫助命令

當您有子命令時,Cobra會自動為您的應用程式新增一個幫助命令。當用戶執行“app help”時會呼叫此方法。此外,幫助還將支援所有其他命令作為輸入。比如說,你有一個名為'create'的命令,沒有任何額外的配置; 當'app help create'被呼叫時,Cobra會工作。每個命令都會自動新增' - help'標誌。

以下輸出由Cobra自動生成。除了命令和標誌定義之外,不需要任何其他內容。

$ cobra help

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 [command]

Available Commands:
  add         Add a command to a Cobra Application
  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
  -l, --license string   name of license for the project
      --viper            use Viper for configuration (default true)

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

幫助就像任何其他命令一樣。它周圍沒有特殊的邏輯或行為。事實上,如果你願意,你可以提供自己的。

定義自己的help

您可以提供自己的help命令或自己的模板,以使用以下函式使用的預設命令:

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

後兩者也適用於任何子命令。

用法

當用戶提供無效標誌或無效命令時,Cobra會通過向用戶顯示“使用情況”來做出響應。

示例

您可以從上面的幫助中認識到這一點。那是因為預設幫助將用法嵌入其輸出中。

$ cobra --invalid
Error: unknown flag: --invalid
Usage:
  cobra [command]

Available Commands:
  add         Add a command to a Cobra Application
  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
  -l, --license string   name of license for the project
      --viper            use Viper for configuration (default true)

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

定義自己的用法

您可以提供自己的使用功能或模板供Cobra使用。與help一樣,函式和模板可以通過公共方法覆蓋:

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

版本flags

如果在root命令上設定了Version欄位,Cobra會新增頂級'--version'標誌。使用'--version'標誌執行應用程式將使用版本模板將版本列印到stdout。可以使用該cmd.SetVersionTemplate(s string)功能自定義模板 。

PreRun和PostRun Hooks

Run命令在主函式之前或之後執行函式。PersistentPreRunPreRun功能執行之前RunPersistentPostRunPostRun將在後執行RunPersistent*Run如果子程式沒有宣告他們自己的功能,他們將繼承這些功能。這些功能按以下順序執行:

PersistentPreRun
PreRun
Run
PostRun
PersistentPostRun

下面是使用所有這些功能的兩個命令的示例。執行子命令時,它將執行root命令,PersistentPreRun但不執行root命令PersistentPostRun: 

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

列印

Inside rootCmd PersistentPreRun with args: []
Inside rootCmd PreRun with args: []
Inside rootCmd Run with args: []
Inside rootCmd PostRun with args: []
Inside rootCmd PersistentPostRun with args: []

Inside rootCmd PersistentPreRun with args: [arg1 arg2]
Inside subCmd PreRun with args: [arg1 arg2]
Inside subCmd Run with args: [arg1 arg2]
Inside subCmd PostRun with args: [arg1 arg2]
Inside subCmd PersistentPostRun with args: [arg1 arg2]

“未知命令”發生時的建議

當“未知命令”錯誤發生時,Cobra將列印自動建議。這使得Cobra git在發生拼寫錯誤時的行為與命令類似。例如:

$ hugo srever
Error: unknown command "srever" for "hugo"

Did you mean this?
        server

Run 'hugo --help' for usage.

根據註冊的每個子命令自動提出建議,並使用Levenshtein距離的實現。每個匹配最小距離為2(忽略大小寫)的註冊命令將顯示為建議。

如果您需要在命令中禁用建議或調整字串距離,請使用:

command.DisableSuggestions = true
#或者
command.SuggestionsMinimumDistance = 1

您還可以使用該SuggestFor屬性顯式設定要為其指定命令的名稱。這允許建議字串距離不接近的字串,但在您的命令集和一些您不想要別名的字串中有意義。例如:

$ kubectl remove
Error: unknown command "remove" for "kubectl"

Did you mean this?
        delete

Run 'kubectl help' for usage.