您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“怎么在Golang中使用Cobra創(chuàng)建CLI應(yīng)用”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“怎么在Golang中使用Cobra創(chuàng)建CLI應(yīng)用”吧!
對(duì)于開(kāi)發(fā)人員來(lái)說(shuō)平時(shí)可能就需要使用到很多 CLI 工具,比如 npm、node、go、python、docker、kubectl 等等,因?yàn)檫@些工具非常小巧、沒(méi)有依賴(lài)性、非常適合系統(tǒng)管理或者一些自動(dòng)化任務(wù)等等。
我們這里選擇使用 Golang 里面非常有名的Cobra庫(kù)來(lái)進(jìn)行 CLI 工具的開(kāi)發(fā)。Cobra 是一個(gè)功能強(qiáng)大的現(xiàn)代化 CLI 應(yīng)用程序庫(kù),有很多知名的 Go 項(xiàng)目使用 Cobra 進(jìn)行構(gòu)建,比如:Kubernetes、Docker、Hugo 等等
Cobra 是構(gòu)建在命令、參數(shù)和標(biāo)識(shí)符之上的:
Commands
表示執(zhí)行動(dòng)作
Args
就是執(zhí)行參數(shù)
Flags
是這些動(dòng)作的標(biāo)識(shí)符
基本的執(zhí)行命令如下所示:
$ APPNAME Command Args --Flags # 或者 $ APPNAME Command --Flags Args
比如我們平時(shí)使用的一些命令行工具:
git clone URL -bare go get -u URL npm install package –save kubectl get pods -n kube-system -l app=cobra
下面我們來(lái)看下 Cobra 的使用,這里我們使用的 go1.13.3 版本,使用 Go Modules 來(lái)進(jìn)行包管理,如果對(duì)這部分知識(shí)點(diǎn)不熟悉的,可以查看前面我們的文章Go Modules 基本使用(視頻)了解。
新建一個(gè)名為my-calc
的目錄作為項(xiàng)目目錄,然后初始化 modules:
$ mkdir my-calc && cd my-calc # 如果 go modules 默認(rèn)沒(méi)有開(kāi)啟,需要執(zhí)行 export GO111MODULE=on 開(kāi)啟 $ go mod init my-calc go: creating new go.mod: module my-calc
初始化完成后可以看到項(xiàng)目根目錄下面多了一個(gè)go.mod
的文件,現(xiàn)在我們還沒(méi)有安裝cobra
庫(kù),執(zhí)行下面的命令進(jìn)行安裝:
# 強(qiáng)烈推薦配置該環(huán)境變量 $ export GOPROXY=https://goproxy.cn $ go get -u github.com/spf13/cobra/cobra
安裝成功后,現(xiàn)在我們可以使用cobra init
命令來(lái)初始化 CLI 應(yīng)用的腳手架:
$ cobra init --pkg-name my-calc Your Cobra applicaton is ready at /Users/ych/devs/workspace/youdianzhishi/course/my-calc
需要注意的是新版本的 cobra 庫(kù)需要提供一個(gè)--pkg-name
參數(shù)來(lái)進(jìn)行初始化,也就是指定上面我們初始化的模塊名稱(chēng)即可。上面的 init 命令就會(huì)創(chuàng)建出一個(gè)最基本的 CLI 應(yīng)用項(xiàng)目:
$ tree . . ├── LICENSE ├── cmd │ └── root.go ├── go.mod ├── go.sum └── main.go 1 directory, 5 files
其中main.go
是 CLI 應(yīng)用的入口,在main.go
里面調(diào)用好了cmd/root.go
下面的Execute
函數(shù):
// main.go package main import "my-calc/cmd" func main() { cmd.Execute() }
然后我們?cè)賮?lái)看下cmd/root.go
文件。
root(根)命令是 CLI 工具的最基本的命令,比如對(duì)于我們前面使用的go get URL
,其中go
就是 root 命令,而get
就是go
這個(gè)根命令的子命令,而在root.go
中就直接使用了 cobra 命令來(lái)初始化rootCmd
結(jié)構(gòu),CLI 中的其他所有命令都將是rootCmd
這個(gè)根命令的子命令了。
這里我們將cmd/root.go
里面的rootCmd
變量?jī)?nèi)部的注釋去掉,并在Run
函數(shù)里面加上一句fmt.Println("Hello Cobra CLI")
:
var rootCmd = &cobra.Command{ Use: "my-calc", Short: "A brief description of your application", Long: `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.`, Run: func(cmd *cobra.Command, args []string) { fmt.Println("Hello Cobra CLI") }, }
這個(gè)時(shí)候我們?cè)陧?xiàng)目根目錄下面執(zhí)行如下命令進(jìn)行構(gòu)建:
$ go build -o my-calc
該命令會(huì)在項(xiàng)目根目錄下生成一個(gè)名為my-calc
的二進(jìn)制文件,直接執(zhí)行這個(gè)二進(jìn)制文件可以看到如下所示的輸出信息:
$ ./my-calc
Hello Cobra CLI
我們知道init
函數(shù)是 Golang 中初始化包的時(shí)候第一個(gè)調(diào)用的函數(shù)。在cmd/root.go
中我們可以看到init
函數(shù)中調(diào)用了cobra.OnInitialize(initConfig)
,也就是每當(dāng)執(zhí)行或者調(diào)用命令的時(shí)候,它都會(huì)先執(zhí)行init
函數(shù)中的所有函數(shù),然后再執(zhí)行execute
方法。該初始化可用于加載配置文件或用于構(gòu)造函數(shù)等等,這完全依賴(lài)于我們應(yīng)用的實(shí)際情況。
在初始化函數(shù)里面cobra.OnInitialize(initConfig)
調(diào)用了initConfig
這個(gè)函數(shù),所有,當(dāng)rootCmd
的執(zhí)行方法RUN: func
運(yùn)行的時(shí)候,rootCmd
根命令就會(huì)首先運(yùn)行initConfig
函數(shù),當(dāng)所有的初始化函數(shù)執(zhí)行完成后,才會(huì)執(zhí)行rootCmd
的RUN: func
執(zhí)行函數(shù)。
我們可以在initConfig
函數(shù)里面添加一些 Debug 信息:
func initConfig() { fmt.Println("I'm inside initConfig function in cmd/root.go") ... }
然后同樣重新構(gòu)建一次再執(zhí)行:
$ go build -o my-calc $ ./my-calc I'm inside initConfig function in cmd/root.go Hello Cobra CLI
可以看到是首先運(yùn)行的是initConfig
函數(shù)里面的信息,然后才是真正的執(zhí)行函數(shù)里面的內(nèi)容。
為了搞清楚整個(gè) CLI 執(zhí)行的流程,我們?cè)?code>main.go里面也添加一些 Debug 信息:
// cmd/root.go func init() { fmt.Println("I'm inside init function in cmd/root.go") cobra.OnInitialize(initConfig) ... } func initConfig() { fmt.Println("I'm inside initConfig function in cmd/root.go") ... } // main.go func main() { fmt.Println("I'm inside main function in main.go") cmd.Execute() }
然后同樣重新構(gòu)建一次再執(zhí)行:
$ go build -o my-calc $ ./my-calc I'm inside init function in cmd/root.go I'm inside main function in main.go I'm inside initConfig function in cmd/root.go Hello Cobra CLI
根據(jù)上面的日志信息我們就可以了解到 CLI 命令的流程了。
init
函數(shù)最后處理的就是flags
了,Flags
就類(lèi)似于命令的標(biāo)識(shí)符,我們可以把他們看成是某種條件操作,在 Cobra 中提供了兩種類(lèi)型的標(biāo)識(shí)符:Persistent Flags
和Local Flags
。
Persistent Flags
: 該標(biāo)志可用于為其分配的命令以及該命令的所有子命令。
Local Flags
: 該標(biāo)志只能用于分配給它的命令。
該函數(shù)主要用于在 home 目錄下面設(shè)置一個(gè)名為.my-calc
的配置文件,如果該文件存在則會(huì)使用這個(gè)配置文件。
// cmd/root.go // initConfig 讀取配置文件和環(huán)境變量 func initConfig() { if cfgFile != "" { // 使用 flag 標(biāo)志中傳遞的配置文件 viper.SetConfigFile(cfgFile) } else { // 獲取 Home 目錄 home, err := homedir.Dir() if err != nil { fmt.Println(err) os.Exit(1) } // 在 Home 目錄下面查找名為 ".my-calc" 的配置文件 viper.AddConfigPath(home) viper.SetConfigName(".my-calc") } // 讀取匹配的環(huán)境變量 viper.AutomaticEnv() // 如果有配置文件,則讀取它 if err := viper.ReadInConfig(); err == nil { fmt.Println("Using config file:", viper.ConfigFileUsed()) } }
viper
是一個(gè)非常優(yōu)秀的用于解決配置文件的 Golang 庫(kù),它可以從 JSON、TOML、YAML、HCL、envfile 以及 Java properties 配置文件中讀取信息,功能非常強(qiáng)大,而且不僅僅是讀取配置這么簡(jiǎn)單,了解更多相關(guān)信息可以查看 Git 倉(cāng)庫(kù)相關(guān)介紹:https://github.com/spf13/viper。
現(xiàn)在我們可以去掉前面我們添加的一些打印語(yǔ)句,我們已經(jīng)創(chuàng)建了一個(gè)my-calc
命令作為rootCmd
命令,執(zhí)行該根命令會(huì)打印Hello Cobra CLI
信息,接下來(lái)為我們的 CLI 應(yīng)用添加一些其他的命令。
在項(xiàng)目根目錄下面創(chuàng)建一個(gè)名為add
的命令,Cobra
添加一個(gè)新的命令的方式為:cobra add <commandName>
,所以我們這里直接這樣執(zhí)行:
$ cobra add add add created at /Users/ych/devs/workspace/youdianzhishi/course/my-calc $ tree . . ├── LICENSE ├── cmd │ ├── add.go │ └── root.go ├── go.mod ├── go.sum ├── main.go └── my-calc 1 directory, 7 files
現(xiàn)在我們可以看到cmd/root.go
文件中新增了一個(gè)add.go
的文件,我們仔細(xì)觀(guān)察可以發(fā)現(xiàn)該文件和cmd/root.go
比較類(lèi)似。首先是聲明了一個(gè)名為addCmd
的結(jié)構(gòu)體變量,類(lèi)型為*cobra.Command
指針類(lèi)型,*cobra.Command
有一個(gè)RUN
函數(shù),帶有*cobra.Command
指針和一個(gè)字符串切片參數(shù)。
然后在init
函數(shù)中進(jìn)行初始化,初始化后,將其添加到rootCmd
根命令中rootCmd.AddCommand(addCmd)
,所以我們可以把addCmd
看成是rootCmd
的子命令。
同樣現(xiàn)在重新構(gòu)建應(yīng)用再執(zhí)行:
$ go build -o my-calc $ ./my-calc Hello Cobra CLI $ ./my-calc add add called
可以看到add
命令可以正常運(yùn)行了,接下來(lái)我們來(lái)讓改命令支持添加一些數(shù)字,我們知道在RUN
函數(shù)中是用戶(hù)字符串 slice 來(lái)作為參數(shù)的,所以要支持添加數(shù)字,我們首先需要將字符串轉(zhuǎn)換為 int 類(lèi)型,返回返回計(jì)算結(jié)果。在cmd/add.go
文件中添加一個(gè)名為intAdd
的函數(shù),定義如下所示:
// cmd/add.go func intAdd(args []string) { var sum int // 循環(huán) args 參數(shù),循環(huán)的第一個(gè)值為 args 的索引,這里我們不需要,所以用 _ 忽略掉 for _, ival := range args { // 將 string 轉(zhuǎn)換成 int 類(lèi)型 temp, err := strconv.Atoi(ival) if err != nil { panic(err) } sum = sum + temp } fmt.Printf("Addition of numbers %s is %d\n", args, sum) }
然后在addCmd
變量中,更新RUN
函數(shù),移除默認(rèn)的打印信息,調(diào)用上面聲明的addInt
函數(shù):
// addCmd Run: func(cmd *cobra.Command, args []string) { intAdd(args) },
然后重新構(gòu)建應(yīng)用執(zhí)行如下所示的命令:
$ go build -o my-calc $ ./my-calc Hello Cobra CLI # 注意參數(shù)之間的空格 $ ./my-calc add 1 2 3 Addition of numbers [1 2 3] is 6
由于RUN
函數(shù)中的args
參數(shù)是一個(gè)字符串切片,所以我們可以傳遞任意數(shù)量的參數(shù),但是確有一個(gè)缺陷,就是只能進(jìn)行整數(shù)計(jì)算,不能計(jì)算小數(shù),比如我們執(zhí)行如下的計(jì)算就會(huì)直接 panic 了:
$ ./my-calc add 1 2 3.5 panic: strconv.Atoi: parsing "3.5": invalid syntax goroutine 1 [running]: my-calc/cmd.intAdd(0xc0000a5890, 0x3, 0x3) ......
因?yàn)樵?code>intAdd函數(shù)里面,我們只是將字符串轉(zhuǎn)換成了 int,而不是 float32/64 類(lèi)型,所以我們可以為addCmd
命令添加一個(gè)flag
標(biāo)識(shí)符,通過(guò)該標(biāo)識(shí)符來(lái)幫助 CLI 確定它是 int 計(jì)算還是 float 計(jì)算。
在cmd/add.go
文件的init
函數(shù)內(nèi)部,我們創(chuàng)建一個(gè) Bool 類(lèi)型的本地標(biāo)識(shí)符,命名成float
,簡(jiǎn)寫(xiě)成f
,默認(rèn)值為 false。這個(gè)默認(rèn)值是非常重要的,意思就是即使沒(méi)有在命令行中調(diào)用 flag 標(biāo)識(shí)符,該標(biāo)識(shí)符的值就將為 false。
// cmd/add.go func init() { rootCmd.AddCommand(addCmd) addCmd.Flags().BoolP("float", "f", false, "Add Floating Numbers") }
然后創(chuàng)建一個(gè)floatAdd
的函數(shù):
func floatAdd(args []string) { var sum float64 for _, fval := range args { // 將字符串轉(zhuǎn)換成 float64 類(lèi)型 temp, err := strconv.ParseFloat(fval, 64) if err != nil { panic(err) } sum = sum + temp } fmt.Printf("Sum of floating numbers %s is %f\n", args, sum) }
該函數(shù)和上面的intAdd
函數(shù)幾乎是相同的,除了是將字符串轉(zhuǎn)換成 float64 類(lèi)型。然后在addCmd
的RUN
函數(shù)中,我們根據(jù)傳入的標(biāo)識(shí)符來(lái)判斷到底應(yīng)該是調(diào)用intAdd
還是floatAdd
,如果傳遞了--float
或者-f
標(biāo)志,就將會(huì)調(diào)用floatAdd
函數(shù)。
// cmd/add.go // addCmd Run: func(cmd *cobra.Command, args []string) { // 獲取 float 標(biāo)識(shí)符的值,默認(rèn)為 false fstatus, _ := cmd.Flags().GetBool("float") if fstatus { // 如果為 true,則調(diào)用 floatAdd 函數(shù) floatAdd(args) } else { intAdd(args) } },
現(xiàn)在重新編譯構(gòu)建 CLI 應(yīng)用,按照如下方式執(zhí)行:
$ go build -o my-calc $ ./my-calc add 1 2 3 Addition of numbers [1 2 3] is 6 $ ./my-calc add 1 2 3.5 -f Sum of floating numbers [1 2 3.5] is 6.500000 $./my-calc add 1 2 3.5 --float Sum of floating numbers [1 2 3.5] is 6.500000
然后接下來(lái)我們?cè)诮oaddCmd
添加一些子命令來(lái)擴(kuò)展它。
添加偶數(shù)
同樣在項(xiàng)目根目錄下執(zhí)行如下命令添加一個(gè)名為even
的命令:
$ cobra add even even created at /Users/ych/devs/workspace/youdianzhishi/course/my-calc
和上面一樣會(huì)在root
目錄下面新增一個(gè)名為even.go
的文件,修改該文件中的init
函數(shù),將rootCmd
修改為addCmd
,因?yàn)槲覀兪菫?code>addCmd添加子命令:
// cmd/even.go func init() { addCmd.AddCommand(evenCmd) }
然后更新evenCmd
結(jié)構(gòu)體參數(shù)的RUN
函數(shù):
// cmd/even.go Run: func(cmd *cobra.Command, args []string) { var evenSum int for _, ival := range args { temp, _ := strconv.Atoi(ival) if temp%2 == 0 { evenSum = evenSum + temp } } fmt.Printf("The even addition of %s is %d\n", args, evenSum) },
首先將字符串轉(zhuǎn)換成整數(shù),然后判斷如果是偶數(shù)才進(jìn)行累加。然后重新編譯構(gòu)建應(yīng)用:
$ go build -o my-calc $ ./my-calc add even 1 2 3 4 5 6 The even addition of [1 2 3 4 5 6] is 12
my-calc
是我們的根命令,add
是rootCmd
的子命令,even
優(yōu)勢(shì)addCmd
的子命令,所以按照上面的方式調(diào)用??梢杂猛瑯拥姆绞皆偃ヌ砑右粋€(gè)奇數(shù)相加的子命令。
到此,相信大家對(duì)“怎么在Golang中使用Cobra創(chuàng)建CLI應(yīng)用”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢(xún),關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。