溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

CLI應用程序的庫Cobra

發(fā)布時間:2020-07-14 13:39:40 來源:網(wǎng)絡 閱讀:4524 作者:recallsong 欄目:編程語言

Cobra既是用于創(chuàng)建強大的現(xiàn)代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是一個庫,提供了一個簡單的接口來創(chuàng)建功能強大的類似于git和go的現(xiàn)代CLI程序。
Cobra也是一個應用程序,幫助你生成應用程序腳手架,以快速開發(fā)基于Cobra的應用程序。
Cobra提供了:

  • 簡單的基于子命令的CLI:app server, app fetch 等。
  • 完全兼容POSIX命令行參數(shù)flag(包括長版本和短版本)
  • 嵌套子命令
  • 全局,局部和及聯(lián)的flags
  • 使用cobra init appname&cobra add cmdname命令很容易生成應用程序
  • 智能提示(app srver... did you mean app server?)
  • 為命令和flag自動生成幫助
  • 自動識別-h,--help標志來顯示幫助信息等
  • 為您的應用程序自動生成bash autocomplete
  • 為您的應用程序自動生成手冊頁
  • 命令別名功能
  • 自定義您的幫助信息,使用信息等
  • 可選的,集成viper來寫12-factor的應用

概念

CobraCobra建立在命令,參數(shù)和標志的結構上。
命令表示動作,參數(shù)是事物,標志是這些動作的修飾符。
最好的應用程序的命令使用起來應該像一個句子一樣。用戶將知道如何使用應用程序,因為很自然就明白意思。
遵循的模式是APPNAME VERB NOUN --ADJECTIVE. 或 APPNAME COMMAND ARG --FLAG
舉幾個現(xiàn)實中比較好的例子來說明這一點。
在下面的例子中,server是一個命令,port是一個標志:

hugo server --port=1313

下面這個命令告訴我們git以bare方式克隆URL

git clone URL --bare

命令

命令是應用程序的中心,應用程序所支持的每一個交互都包含在命令中,一個命令可以有子命令,并可以選擇運行一個動作。
在上面的例子中,server是一個命令
關于cobra.Command文檔

Flag

標志是修改命令行為的一種方式。Cobra完全符合POSIX的標志和Go的標志。Cobra命令可以定義所有命令都能使用的persist標志,也能定義僅對某命令有效的標志。
在上面的例子中,port是標志。
標志功能由pflag庫提供,這是標準庫中flag的一個分支,它在保持相同接口的同時增加了POSIX合規(guī)性。

安裝

使用Cobra很容易。首先,使用go get安裝最新版本的庫。該命令將安裝可執(zhí)行的cobra生成器,庫和它的依賴庫:

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

接下來,在你的應用程序中國年包含Cobra:

import "github.com/spf13/cobra"

入門

盡管歡迎您提供自己的組織,但通?;贑obra的應用程序將遵循以下組織結構:

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

在Cobra應用程序中,通暢main.go文件非常簡單,它只有一個目的:初始化Cobra。

package main

import (
  "fmt"
  "os"

  "{pathToYourApp}/cmd"
)

func main() {
  cmd.Execute()
}

使用Cobra生成器

Cobra提供了自己的程序,它將創(chuàng)建你的應用程序并添加你想要的任何命令。這是將Cobra整合到您的應用程序中的最簡單方法。

Cobra初始

cobra init [app] 命令將為你創(chuàng)建初始的應用程序代碼。這是一個功能非常強大的應用程序,它可以使您的程序具有適當?shù)慕Y構,因此您可以立即享受到Cobra的所有好處。它也會自動將您指定的許可證應用于你的應用程序。
Cobra init非常聰明。您可以提供完整的路徑,或者只是一個類似于導入路徑:

cobra init github.com/spf13/newApp

Cobra添加

應用程序初始化后,Cobra可以為您創(chuàng)建其他命令。假設您創(chuàng)建了一個應用程序,并且需要以下命令:

  • app serve
  • app config
  • app config create
    在您的項目目錄中(您的main.go文件所在的位置),您可以運行以下命令:

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

    注意:對于命令名稱使用camelCase(不是snake_case / snake-case)。否則,你會遇到錯誤。例如,cobra add add-user不正確,但是cobra add addUser有效。
    一旦你運行了這三個命令,你就會有一個類似于以下的應用程序結構:

    ? app/
    ? cmd/
        serve.go
        config.go
        create.go
      main.go

    此時你可以運行go run main.go,它會運行你的應用程序。go run main.go serve,go run main.go config,go run main.go config create以及go run main.go help serve等都可以執(zhí)行。
    顯然你還沒有添加你自己的代碼。但這些命令已準備好了。

    配置Cobra

    如果您提供一個簡單的配置文件,Cobra生成器將更易于使用,這將幫助您避免在標志中反復提供大量重復信息。
    例如~/.cobra.yaml文件:

    author: Steve Francia <spf@spf13.com>
    license: MIT

    您可以通過設置license為指定任何許可證,none或者您也可以指定自定義許可證:

    license:
    header: This file is part of {{ .appName }}.
    text: |
    {{ .copyright }}
    
    This is my license. There are many like it, but this one is mine.
    My license is my best friend. It is my life. I must master it as I must
    master my life.

    您也可以使用內置許可證。例如,GPLv2,GPLv3,LGPL, AGPL,MIT,2-Clause BSD或3-Clause BSD。

    使用Cobra庫

    要手動實現(xiàn)Cobra您需要提供一個簡潔的mai.go文件和一個rootCmd文件。如果你認為合適,你也可以選擇其他命令。

    創(chuàng)建rootCmd

    Cobra不需要任何構造函數(shù),只需要創(chuàng)建你的命令。
    理想情況下,應該把它放在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函數(shù)中定義標志并處理配置:
    例如 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)
        }
    }

    創(chuàng)建你的main.go

    您需要讓主函數(shù)執(zhí)行root命令。盡管可以在任何命令上調用Execute,但為了清晰起見,應該在根上運行Execute。

在Cobra應用程序中,通常main.go文件非常簡潔。它用于初始化Cobra。

package main

import (
  "fmt"
  "os"

  "{pathToYourApp}/cmd"
)

func main() {
  cmd.Execute()
}

創(chuàng)建其他命令

可以定義其他命令,并且通常分別寫在cmd/目錄中的不同文件里。
如果您像創(chuàng)建一個版本命令,你可以創(chuàng)建 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")
        },
    }

使用標志

標志是用來控制命令行為的一種方式。

將標志分配給命令

由于標志是在不同位置定義和使用的,因此我們需要在正確的范圍外定義一個變量來分配給標志。

var Verbose bool
var Source string

有兩種不同的方法來分配一個標志。

持久標志

一個標志是可以持久的,著意味著這個標志將可以用于它分配的命令以及該命令下的每個命令。對于全局標志,在根上分配一個標志作為持久標志。

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

本地標志

一個標志也可以在本地分配,只適用于該特定命令。

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

默認情況下,Cobra僅解析目標命令上的本地標志,父命令上的任何本地標志都將被忽略。通過啟用Command.TraverseChildren,將在執(zhí)行目標命令之前解析每個命令上的本地標志。

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

使用Config綁定標志

你可以綁定你的flag到viper。

var author string

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

在這個例子中,持久標志author被綁定到了viper。請注意,如果用戶沒有從viper中提供值,則該author不會被設置。
了解更多請看viper文檔

必需的標志

標志默認是可選的,如果你希望在沒有提供標志時報錯,請將標志標記為必需的:

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

位置和自定義參數(shù)

位置參數(shù)的校驗可以使用Command的Args字段。
以下是內置的校驗器:

  • NoArgs - 如果有任何參數(shù),該命令將報告錯誤。
  • ArbitraryArgs - 該命令將接受任何參數(shù)。
  • OnlyValidArgs- 如果有任何不在ValidArgs字段中的參數(shù),則該命令將報告錯誤。
  • MinimumNArgs(int) - 如果沒有至少N個位置參數(shù),該命令將報告錯誤。
  • MaximumNArgs(int) - 如果存在多于N個位置參數(shù),則該命令將報告錯誤。
  • ExactArgs(int) - 如果不存在完全N個位置參數(shù),則該命令將報告錯誤。
  • RangeArgs(min, max) - 如果參數(shù)的數(shù)量這個某個范圍之間,則該命令將報告錯誤。
    設置自定義校驗器的示例:
    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就是頂層命令的子命令之一。在這種情況下,根不可執(zhí)行,這意味著需要子命令。這是通過不為'rootCmd'提供'Run'來實現(xiàn)的。

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

有關標志的更多文檔可在https://github.com/spf13/pflag 中找到。

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'運行時被調用。每個命令都會自動添加'--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.

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

自定義幫助命令

你可以提供自己的幫助命令或自己的模版,用一下函數(shù):

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

后面兩個函數(shù)也應用于任何子命令。

使用信息

當用戶提供無效的標志或無效的命令時,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.

定義自己的用法信息

您可以提供您自己的用法函數(shù)或模板。像幫助一樣,函數(shù)和模板可以通過方法重寫:

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

版本標志

如果Version字段在根命令上設置,則Cobra將添加頂級“--version”標志。使用'--version'標志運行應用程序將使用版本模板將版本打印到標準輸出。該模板可以使用該cmd.SetVersionTemplate(s string)功能進行定制 。

PreRun和PostRun鉤子

可以命令Run主要功能之前或之后運行函數(shù)。該PersistentPreRun和PreRun函數(shù)在Run之前被執(zhí)行。PersistentPostRun并PostRun會在Run之后被執(zhí)行。Persistent*Run如果子命令沒有聲明他們自己的函數(shù),這些函數(shù)將被繼承。這些函數(shù)按以下順序運行:

  • PersistentPreRun
  • PreRun
  • Run
  • PostRun
  • PersistentPostRun
    以下是使用所有這些特性的兩個命令的示例。當子命令執(zhí)行時,它將運行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()
    }

    出現(xiàn)"unknown command"時的建議

    當“unknown command”錯誤發(fā)生時,Cobra將打印自動建議。這使得Cobra于git在發(fā)生錯字時的表現(xiàn)類似。例如:

    $ hugo srever
    Error: unknown command "srever" for "hugo"
    
    Did you mean this?
                    server
    
    Run 'hugo --help' for usage.

    建議是根據(jù)每個已注冊的子命令自動生成的,并使用Levenshtein distance的實現(xiàn)。每個注冊的命令的最小距離為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.

為您的命令生成文檔

Cobra可以使用以下格式生成基于子命令、標志等的文檔:

  • Markdown
  • ReStructured Text
  • Man Page

    生成bash完成

    Cobra可以生成一個bash完成文件。如果您在命令中添加更多信息,這些完成功能可以非常強大且靈活。在Bash完成中閱讀更多關于它的信息。

    貢獻

    1、Fork倉庫
    2、克隆你fork的倉庫到你的電腦上(git clone https://github.com/your_username/cobra && cd cobra)
    3、創(chuàng)建您的功能分支(git checkout -b my-new-feature)
    4、進行更改并添加它們(git add .)
    5、提交您的更改(git commit -m 'Add some feature')
    6、推送到分支(git push origin my-new-feature)
    7、創(chuàng)建新的pull request

    License

    Cobra 是在Apache 2.0許可下發(fā)布的。請參閱LICENSE.txt

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。

AI