概念
cobra 構建中,有三塊基石頭:命令、參數和標志。要使用 Cobra 編寫一個命令行程序,首先要明確這三個概念:
- 命令(COMMAND):命令表示要執行的操作。
- 參數(ARG):是命令的參數,一般用來表示操作的對象。
- 標志(FLAG):是命令的修飾,可以調整操作的行為。
一個好的命令行程序在使用時讀起來像句子,用戶會自然的理解并知道如何使用該程序。比如:
git log master --oneline --graph
其中:
log 是命令,表明執行的操作是查看日志;
master 是參數, 指明log操作的對象是master分支;
--oneline是標志,用于限定log的行為,以簡潔單行的方式顯示;
--graph也是標志,用于限定log的行為,以圖形化方式顯示提交和分支關系。
要編寫一個好的命令行程序,通常遵循的模式是:
APPNAME VERB NOUN --ADJECTIVE 或 APPNAME COMMAND ARG --FLAG
在這里 VERB 代表動詞,NOUN 代表名詞,ADJECTIVE 代表形容詞。
另一方面,在實際應用中,如果一個命令有多個參數,為了方便使用和控制,通常會用具名參數的形式,即參數選項化:
ssh -p 1000 smbody@192.168.1.1
hull create “name” “email” “class” --> hull create "name" -E "email" -c "class"
快速開始
要使用 cobra 創建命令行程序,需要先通過如下命令進行安裝:
$ go get -u github.com/spf13/cobra/cobra
安裝好后,就可以像其他 Go 語言庫一樣導入 cobra 包并使用了。
import "github.com/spf13/cobra"
創建一個命令
以官網例子,假設我們要創建的命令行程序叫作 hugo,可以編寫如下代碼創建一個命令:
hugo/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 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)
}
}
cobra.Command 是一個結構體,代表一個命令,其各個屬性含義如下:
Use 是命令的名稱。
Short 代表當前命令的簡短描述。
Long 表示當前命令的完整描述。
Run 屬性是一個函數,當執行命令時會調用此函數。
rootCmd.Execute() 是命令的執行入口,其內部會解析 os.Args[1:] 參數列表(默認情況下是這樣,也可以通過 Command.SetArgs 方法設置參數),然后遍歷命令樹,為命令找到合適的匹配項和對應的標志。
創建 main.go
按照編寫 Go 程序的慣例,我們要為 hugo 程序編寫一個 main.go 文件,作為程序的啟動入口。
package main
import (
"hugo/cmd"
)
func main() {
cmd.Execute()
}
main.go 代碼實現非常簡單,只在 main 函數中調用了 cmd.Execute() 函數,來執行命令。
編譯并運行命令
現在,我們就可以編譯并運行這個命令行程序了。
go build -o hugo main.go
$ ./hugo --help
A Fast and Flexible Static Site Generator built with
love by spf13 and friends in Go.
Complete documentation is available at gohugo.io
Usage:
hugo [flags]
Flags:
-h, --help help for hugo
命令與子命令
與定義 rootCmd 一樣,我們可以使用 cobra.Command 定義其他命令,并通過 rootCmd.AddCommand() 方法將其添加為 rootCmd 的一個子命令。
hugo/cmd/version.go
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)
}
現在重新編譯并運行命令行程序。
$ ./hugo -h
A Fast and Flexible Static Site Generator built with
love by spf13 and friends in Go.
Complete documentation is available at 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.
可以看見version命令已經被加進來了。運行version子命令:
$ ./hugo version
Hugo Static Site Generator v0.9 -- HEAD
當然,子命令可以繼續添加子命令,從而實現多級命令嵌套。cobra 會自動進行命令樹展開,并匹配對應的選項。
在設計多級命令時,通常按一下規則:
APPNAME GROUP... ACTION ARGs [FLAGS]
標志
標志配置和獲取值
cobra.Command 提供了Flags() 接口及一系列選項配置方法,來為指定的命令設置選項。例如:
subCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")
為subCmd 配置一個 --source/-s 選項,選項的值是 字符串類型,默認值是“”;help 說明為“Source directory to read from”。
cobra.Command 綁定的run函數, 原型為:
func(command *cobra.Command, args []string) {
}
通常,實現的run函數中,會從 command 中獲取相關的選項內容,用于邏輯控制:
name, err := command.Flags().GetString("grep-name")
if err != nil {
ErrorExit("參數或選項解析錯誤:" + err.Error())
}
持久標志
如果一個標志是持久的,則意味著該標志將可用于它所分配的命令以及該命令下的所有子命令。
對于全局標志,可以定義在根命令 rootCmd 上。
var Verbose bool
rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
這樣,所有命令都可以繼承和使用該標志。
本地標志
標志也可以是本地的,這意味著它只適用于該指定命令。
var Source string
subCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")
必選標志
默認情況下,標志是可選的。我們可以將其標記為必選,如果沒有提供,則會報錯。
var Region string
subCmd.Flags().StringVarP(&Region, "region", "r", "", "AWS region (required)")
subCmd.MarkFlagRequired("region")
參數驗證
在執行命令行程序時,我們可能需要對命令參數進行合法性驗證,cobra.Command 的 Args 屬性提供了此功能。
Args 屬性類型為一個函數:func(cmd *Command, args []string) error,可以用來驗證參數。
Cobra 內置了以下驗證函數:
NoArgs:如果存在任何命令參數,該命令將報錯。ArbitraryArgs:該命令將接受任意參數。OnlyValidArgs:如果有任何命令參數不在Command的ValidArgs字段中,該命令將報錯。MinimumNArgs(int):如果沒有至少 N 個命令參數,該命令將報錯。MaximumNArgs(int):如果有超過 N 個命令參數,該命令將報錯。ExactArgs(int):如果命令參數個數不為 N,該命令將報錯。ExactValidArgs(int):如果命令參數個數不為 N,或者有任何命令參數不在Command的ValidArgs字段中,該命令將報錯。RangeArgs(min, max):如果命令參數的數量不在預期的最小數量min和最大數量max之間,該命令將報錯。- 內置驗證函數用法如下:
var versionCmd = &cobra.Command{
Use: "version",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Hugo Static Site Generator v0.9 -- HEAD")
},
Args: cobra.MaximumNArgs(2), // 使用內置的驗證函數,位置參數多于 2 個則報錯
}
當然,我們也可以自定義驗證函數:
var printCmd = &cobra.Command{
Use: "print [OPTIONS] [COMMANDS]",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("run print...")
// 命令行位置參數列表:例如執行 `hugo print a b c d` 將得到 [a b c d]
fmt.Printf("args: %v\n", args)
},
// 使用自定義驗證函數
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("requires at least one arg")
}
if len(args) > 4 {
return errors.New("the number of args cannot exceed 4")
}
if args[0] != "a" {
return errors.New("first argument must be 'a'")
}
return nil
},
}
Hooks
在執行 Run 函數前后,我么可以執行一些鉤子函數,其作用和執行順序如下:
PersistentPreRun:在PreRun函數執行之前執行,對此命令的子命令同樣生效。PreRun:在Run函數執行之前執行。Run:執行命令時調用的函數,用來編寫命令的業務邏輯。PostRun:在Run函數執行之后執行。PersistentPostRun:在PostRun函數執行之后執行,對此命令的子命令同樣生效。
可以使用如下方式測試:
var rootCmd = &cobra.Command{
Use: "hugo",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
fmt.Println("hugo PersistentPreRun")
},
PreRun: func(cmd *cobra.Command, args []string) {
fmt.Println("hugo PreRun")
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("run hugo...")
},
PostRun: func(cmd *cobra.Command, args []string) {
fmt.Println("hugo PostRun")
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
fmt.Println("hugo PersistentPostRun")
},
}
通常,可以在root command 中,指定PresistentPreRun 方法,用于執行一些公共處理邏輯(比如,檢查登錄狀態等),達到初始化或者配置的目的。
自定義usage 或help
定義自己的 Help 命令
如果你對 obra 自動生成的幫助命令不滿意,我們可以自定義幫助命令或模板。
cmd.SetHelpCommand(cmd *Command)
cmd.SetHelpFunc(f func(*Command, []string))
cmd.SetHelpTemplate(s string)
obra 提供了三個方法來實現自定義幫助命令,后兩者也適用于任何子命令。
默認情況下,我們可以使用 hugo help command 語法查看子命令的幫助信息,也可以使用 hugo command -h/--help 查看。
使用 help 命令查看幫助信息:
$ ./hugo help version
hugo PersistentPreRunE
All software has versions. This is Hugo's
Usage:
hugo version [flags]
Flags:
-h, --help help for version
Global Flags:
--author string Author name for copyright attribution (default "YOUR NAME")
-v, --verbose verbose output
hugo PersistentPostRun
使用 -h/--help 查看幫助信息:
$ ./hugo version -h
All software has versions. This is Hugo's
Usage:
hugo version [flags]
Flags:
-h, --help help for version
Global Flags:
--author string Author name for copyright attribution (default "YOUR NAME")
-v, --verbose verbose output
對比發現,使用 help [cmd]時,會執行命令的Hook 函數,而使用 [cmd] help 不會。
定義自己的 Usage Message
當用戶提供無效標志或無效命令時,cobra 通過向用戶顯示 Usage 來提示用戶如何正確的使用命令。
與 help 信息一樣,我們也可以進行自定義。cobra 提供了如下兩個方法,來控制輸出:
cmd.SetUsageFunc(f func(*Command) error)
cmd.SetUsageTemplate(s string)
未知命令建議
在我們使用 git 命令時,有一個非常好用的功能,能夠對用戶輸錯的未知命令智能提示。
$ git statu
git: 'statu' is not a git command. See 'git --help'.
The most similar commands are
status
stage
stash
這個功能非常實用,幸運的是,cobra 自帶了此功能。
如果你想徹底關閉此功能,可以使用如下設置:
command.DisableSuggestions = true
或者使用如下設置調整字符串匹配的最小距離:
command.SuggestionsMinimumDistance = 1
SuggestionsMinimumDistance 是一個正整數,表示輸錯的命令與正確的命令最多有幾個不匹配的字符(最小距離),才會給出建議。如當值為 1 時,用戶輸入 hugo versiox 會給出建議,而如果用戶輸入 hugo versixx 時,則不會給出建議,因為已經有兩個字母不匹配 version 了。
Shell 補全
cobra默認提供 completion 子命令,可以為指定的 Shell 生成自動補全腳本,現在我們就來講解它的用法。
直接執行 hugo completion 命令,我們可以查看它支持的幾種 Shell 類型 bash、fish、powershell、zsh。
首先,明確自己環境中所使用的Shell類型,可以用以下方式查看
$ echo $0
/bin/zsh
然后,可以根據自己的shell類型,查看如何生產completion 腳本
./hugo completion zsh -h
Generate the autocompletion script for the zsh shell.
If shell completion is not already enabled in your environment you will need
to enable it. You can execute the following once:
echo "autoload -U compinit; compinit" >> ~/.zshrc
To load completions in your current shell session:
source <(hugo completion zsh)
To load completions for every new session, execute once:
#### Linux:
hugo completion zsh > "${fpath[1]}/_hugo"
#### macOS:
hugo completion zsh > $(brew --prefix)/share/zsh/site-functions/_hugo
You will need to start a new shell for this setup to take effect.
Usage:
hugo completion zsh [flags]
Flags:
-h, --help help for zsh
--no-descriptions disable completion descriptions
Global Flags:
--author string Author name for copyright attribution (default "YOUR NAME")
-v, --verbose verbose output
最后,執行對應的指令,生產completion 腳本,并配置 .profile 或者 .zshrc 或者.bashrc (根據自己的環境),加載completion腳本,即可實現命令自動補全。
最后
本打算附上官方地址,但無法過審。GH 上搜索cobra, 認準spf13/cobra.