Go學習筆記 : cobra 包簡介

cobra 是 go 語言的一個庫,可以用於編寫命令行工具。通常我們可以看到git pull 、docker container start 、apt install 等等這樣命令,都可以很容易用corba來實現,另外,go 語言是很容易編譯成一個二進制文件,本文將實現一個簡單的命令行工具。

主要功能

cobra 的主要功能如下,可以說每一項都很實用:

  • 簡易的子命令行模式,如 app server, app fetch 等等
  • 完全兼容 posix 命令行模式
  • 嵌套子命令 subcommand
  • 支持全局,局部,串聯 flags
  • 使用 cobra 很容易的生成應用程序和命令,使用 cobra create appname 和 cobra add cmdname
  • 如果命令輸入錯誤,將提供智能建議,如 app srver,將提示 srver 沒有,是不是 app server
  • 自動生成 commands 和 flags 的幫助信息
  • 自動生成詳細的 help 信息,如 app help
  • 自動識別幫助 flag -h,--help
  • 自動生成應用程序在 bash 下命令自動完成功能
  • 自動生成應用程序的 man 手冊
  • 命令行別名
  • 自定義 help 和 usage 信息
  • 可選的與 viper apps 的緊密集成

cobra 中的主要概念

cobra 中有個重要的概念,分別是 commands、arguments 和 flags。其中 commands 代表行爲,arguments 就是命令行參數(或者稱爲位置參數),flags 代表對行爲的改變(也就是我們常說的命令行選項)。執行命令行程序時的一般格式爲:
APPNAME COMMAND ARG --FLAG
比如下面的例子:

# server是 commands,port 是 flag
hugo server --port=1313
 
# clone 是 commands,URL 是 arguments,brae 是 flag
git clone URL --bare

如果是一個簡單的程序(功能單一的程序),使用 commands 的方式可能會很囉嗦,但是像 git、docker 等應用,把這些本就很複雜的功能劃分爲子命令的形式,會方便使用(對程序的設計者來說又何嘗不是如此)。

創建 cobra 應用

在創建 cobra 應用前需要先安裝 cobra 包:    go get -u github.com/spf13/cobra/cobra

然後就可以用 cobra 程序生成應用程序框架了:   cobra init --pkg-name cobrademo  [在cobrademo目錄下執行]

使用 cobra 程序生成命令代碼

除了生成應用程序框架,還可以通過 cobra add 命令生成子命令的代碼文件,比如下面的命令會添加兩個子命令 image 和 container 相關的代碼文件:  cobra add image 和 cobra add container

這兩條命令分別生成了 cobrademo 程序中 image 和 container 子命令的代碼,當然了,具體的功能還得靠我們自己實現。

爲命令添加具體的功能

到目前爲止,我們一共爲 cobrademo 程序添加了三個 Command,分別是 rootCmd(cobra init 命令默認生成)、imageCmd 和 containerCmd。
打開文件 root.go ,找到變量 rootCmd 的初始化過程併爲之設置 Run 方法:

Run: func(cmd *cobra.Command, args []string) {
    fmt.Println("cobra demo program")
},

重新編譯 cobrademo 程序並不帶參數運行【初次要執行 go mod init】,這次就不再輸出幫助信息了,而是執行了 rootCmd 的 Run 方法:

D:\Project\GoProject\src\cobrademo>go build -o .
 
D:\Project\GoProject\src\cobrademo>cobrademo.exe
cobra demo program

再創建一個 version Command 用來輸出當前的軟件版本。先在 cmd 目錄下添加 version.go 文件[ cobra add version],編輯文件的內容如下:

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 cobrademo",
    Long:  `All software has versions. This is cobrademo's`,
    Run: func(cmd *cobra.Command, args []string) {
        fmt.Println("cobrademo version is v1.0")
    },
}
D:\Project\GoProject\src\cobrademo>go run main.go version
cobrademo version is v1.0

爲 Command 添加選項(flags)

選項(flags)用來控制 Command 的具體行爲。根據選項的作用範圍,可以把選項分爲兩類:

  • persistent
  • local

對於 persistent 類型的選項,既可以設置給該 Command,又可以設置給該 Command 的子 Command。對於一些全局性的選項,比較適合設置爲 persistent 類型,比如控制輸出的 verbose 選項:

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

local 類型的選項只能設置給指定的 Command,比如下面定義的 source 選項:

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

該選項不能指定給 rootCmd 之外的其它 Command。

默認情況下的選項都是可選的,但一些用例要求用戶必須設置某些選項,這種情況 cobra 也是支持的,通過 Command 的 MarkFlagRequired 方法標記該選項即可:

var Name string
rootCmd.Flags().StringVarP(&Name, "name", "n", "", "user name (required)")
rootCmd.MarkFlagRequired("name")

命令行參數(arguments)

首先我們來搞清楚命令行參數(arguments)與命令行選項的區別(flags/options)。以常見的 ls 命令來說,其命令行的格式爲:
ls [OPTION]... [FILE]…
其中的 OPTION 對應本文中介紹的 flags,以 - 或 -- 開頭;而 FILE 則被稱爲參數(arguments)或位置參數。一般的規則是參數在所有選項的後面,上面的 … 表示可以指定多個選項和多個參數。

cobra 默認提供了一些驗證方法:

  • NoArgs - 如果存在任何位置參數,該命令將報錯
  • ArbitraryArgs - 該命令會接受任何位置參數
  • OnlyValidArgs - 如果有任何位置參數不在命令的 ValidArgs 字段中,該命令將報錯
  • MinimumNArgs(int) - 至少要有 N 個位置參數,否則報錯
  • MaximumNArgs(int) - 如果位置參數超過 N 個將報錯
  • ExactArgs(int) - 必須有 N 個位置參數,否則報錯
  • ExactValidArgs(int) 必須有 N 個位置參數,且都在命令的 ValidArgs 字段中,否則報錯
  • RangeArgs(min, max) - 如果位置參數的個數不在區間 min 和 max 之中,報錯

一個完整的 demo

我們在前面創建的代碼的基礎上,爲 image 命令添加行爲(打印信息到控制檯),併爲它添加一個子命令 cmdTimes,下面是更新後的 image.go 文件的內容:

package cmd
 
import (
    "fmt"
    "strings"
 
    "github.com/spf13/cobra"
)
 
// imageCmd represents the image command
var imageCmd = &cobra.Command{
    Use:   "image",
    Short: "Print images information",
    Long:  "Print all images information",
    Run: func(cmd *cobra.Command, args []string) {
        fmt.Println("image one is  win7")
        fmt.Println("image two is win10")
        fmt.Println("image args are : " + strings.Join(args, " "))
    },
}
 
var echoTimes int
var cmdTimes = &cobra.Command{
    Use:   "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, " "))
        }
    },
}
 
func init() {
    rootCmd.AddCommand(imageCmd)
    cmdTimes.Flags().IntVarP(&echoTimes, "times", "t", 1, "times to echo the input")
    imageCmd.AddCommand(cmdTimes)
}

編譯後執行命令:

D:\Project\GoProject\src\cobrademo>go run main.go image hello
image one is  win7
image two is win10
image args are : hello
 
D:\Project\GoProject\src\cobrademo>go run main.go image times -t=3 world
Echo: world
Echo: world
Echo: world
 
D:\Project\GoProject\src\cobrademo>go run main.go image times -t=3
Error: requires at least 1 arg(s), only received 0
Usage:
  cobrademo image times [string to echo] [flags]
 
Flags:
  -h, --help        help for times
  -t, --times int   times to echo the input (default 1)
 
Global Flags:
      --config string   config file (default is $HOME/.cobrademo.yaml)
 
requires at least 1 arg(s), only received 0
exit status 1
 
D:\Project\GoProject\src\cobrademo>

因爲我們爲 cmdTimes 命令設置了 Args: cobra.MinimumNArgs(1),所以必須爲 times 子命令傳入一個參數,不然 times 子命令會報錯:

幫助信息(help command)

cobra 會自動添加 --help(-h)選項,所以我們可以不必添加該選項而直接使用:

 
D:\Project\GoProject\src\cobrademo>go run main.go -h
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.
 
Usage:
  cobrademo [flags]
  cobrademo [command]
 
Available Commands:
  container   A brief description of your command
  help        Help about any command
  image       Print images information
  version     Print the version number of cobrademo
 
Flags:
      --config string   config file (default is $HOME/.cobrademo.yaml)
  -h, --help            help for cobrademo
  -t, --toggle          Help message for toggle
 
Use "cobrademo [command] --help" for more information about a command.

cobra 同時還自動添加了 help 子命,默認效果和使用 --help 選項相同。如果爲 help 命令傳遞其它命令作爲參數,則會顯示對應命令的幫助信息,下面的命令輸出 image 子命令的幫助信息:

D:\Project\GoProject\src\cobrademo>go run main.go help image
Print all images information
 
Usage:
  cobrademo image [flags]
  cobrademo image [command]
 
Available Commands:
  times       Echo anything to the screen more times
 
Flags:
  -h, --help   help for image
 
Global Flags:
      --config string   config file (default is $HOME/.cobrademo.yaml)
 
Use "cobrademo image [command] --help" for more information about a command.

當然也可以通過這種方式查看子命令的子命令的幫助文檔:go run main.go help image times

除了 cobra 默認的幫助命令,我們還可以通過下面的方式進行自定義:

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

提示信息(usage message)

提示信息和幫助信息很相似,只不過它是在你輸入了非法的參數、選項或命令時纔出現的,和幫助信息一樣,我們也可以通過下面的方式自定義提示信息:

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

在 Commnad 執行前後執行額外的操作

Command 執行的操作是通過 Command.Run 方法實現的,爲了支持我們在 Run 方法執行的前後執行一些其它的操作,Command 還提供了額外的幾個方法,它們的執行順序如下:
    1. PersistentPreRun
    2. PreRun
    3. Run
    4. PostRun
    5. PersistentPostRun
修改 rootCmd 的初始化代碼如下:

var rootCmd = &cobra.Command{
    Use:   "cobrademo",
    Short: "sparkdev's cobra demo",
    Long: "the demo show how to use cobra package",
    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("cobra demo program, 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)
    },
}

重新編譯 cobrademo 程序並執行,輸出結果中可以看到這些方法的執行順序

D:\Project\GoProject\src\cobrademo>go run main.go
Inside rootCmd PersistentPreRun with args: []
Inside rootCmd PreRun with args: []
cobra demo program, with args: []
Inside rootCmd PostRun with args: []
Inside rootCmd PersistentPostRun with args: []
 
D:\Project\GoProject\src\cobrademo>go run main.go image hello
Inside rootCmd PersistentPreRun with args: [hello]
image one is  win7
image two is win10
image args are : hello
Inside rootCmd PersistentPostRun with args: [hello]
 
D:\Project\GoProject\src\cobrademo>

其中的 PersistentPreRun 方法和 PersistentPostRun 方法會伴隨任何子命令的執行:

對未知命令的提示

如果我們輸入了不正確的命令或者是選項,cobra 還會智能的給出提示,對於這樣的提示我們也是可以自定義的,或者如果覺着沒用就直接關閉掉。

參考:

https://www.cnblogs.com/sparkdev/p/10856077.html

https://xuchao918.github.io/2019/04/29/使用-go-cobra創建命令行項目/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章