亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

在?Golang?中使用?Cobra?創(chuàng)建?CLI?應(yīng)用

 更新時(shí)間:2022年01月04日 14:59:27   作者:吱吱吱?(piperck)?XD  
這篇文章主要介紹了在?Golang?中使用?Cobra?創(chuàng)建?CLI?應(yīng)用,來(lái)看下?Cobra?的使用,這里我們使用的?go1.13.3?版本,使用?Go?Modules?來(lái)進(jìn)行包管理,需要的朋友可以參考下

雖然現(xiàn)在我們使用的大多數(shù)軟件都是可視化的,很容易上手,但是這并不代表 CLI(命令行)應(yīng)用就沒(méi)有用武之地了,特別是對(duì)于開(kāi)發(fā)人員來(lái)說(shuō),還是會(huì)經(jīng)常和 CLI 應(yīng)用打交道。而 Golang 就非常適合用來(lái)構(gòu)建 CLI 應(yīng)用,下面我們就將來(lái)介紹如何在 Golang 中構(gòu)建一個(gè) CLI 應(yīng)用。

對(duì)于開(kāi)發(fā)人員來(lái)說(shuō)平時(shí)可能就需要使用到很多 CLI 工具,比如 npm、node、go、python、docker、kubectl 等等,因?yàn)檫@些工具非常小巧、沒(mé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)行初始化,也就是指定上面我們初始化的模塊名稱即可。上面的 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文件。

rootCmd

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

我們知道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ù)等等,這完全依賴于我們應(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í)行rootCmdRUN: 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就類似于命令的標(biāo)識(shí)符,我們可以把他們看成是某種條件操作,在 Cobra 中提供了兩種類型的標(biāo)識(shí)符:Persistent FlagsLocal Flags。

  • Persistent Flags: 該標(biāo)志可用于為其分配的命令以及該命令的所有子命令。
  • Local Flags: 該標(biāo)志只能用于分配給它的命令。

initConfig

該函數(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)用添加一些其他的命令。

添加數(shù)據(jù)

在項(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ì)觀察可以發(fā)現(xiàn)該文件和cmd/root.go比較類似。首先是聲明了一個(gè)名為addCmd的結(jié)構(gòu)體變量,類型為*cobra.Command指針類型,*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ù)中是用戶字符串 slice 來(lái)作為參數(shù)的,所以要支持添加數(shù)字,我們首先需要將字符串轉(zhuǎn)換為 int 類型,返回返回計(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 類型
		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 類型,所以我們可以為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 類型的本地標(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 類型
		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 類型。然后在addCmdRUN函數(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是我們的根命令,addrootCmd的子命令,even優(yōu)勢(shì)addCmd的子命令,所以按照上面的方式調(diào)用??梢杂猛瑯拥姆绞皆偃ヌ砑右粋€(gè)奇數(shù)相加的子命令。

到這里我們就在 Golang 里面使用Cobra創(chuàng)建了一個(gè)簡(jiǎn)單的 CLI 應(yīng)用。本文的內(nèi)容雖然比較簡(jiǎn)單,但是是我們了解學(xué)習(xí)Cobra基礎(chǔ)的一個(gè)很好的入門(mén)方式,后續(xù)我們也可以嘗試添加一些更加復(fù)雜的使用案例。

Reference:

https://www.qikqiak.com/post/create-cli-app-with-cobra在 Golang 中使用 Cobra 創(chuàng)建 CLI 應(yīng)用

https://cobra.dev cobra docs

到此這篇關(guān)于在 Golang 中使用 Cobra 創(chuàng)建 CLI 應(yīng)用的文章就介紹到這了,更多相關(guān)Golang創(chuàng)建 CLI 應(yīng)用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang設(shè)計(jì)模式之生成器模式講解和代碼示例

    Golang設(shè)計(jì)模式之生成器模式講解和代碼示例

    生成器是一種創(chuàng)建型設(shè)計(jì)模式,使你能夠分步驟創(chuàng)建復(fù)雜對(duì)象,與其他創(chuàng)建型模式不同,生成器不要求產(chǎn)品擁有通用接口,這使得用相同的創(chuàng)建過(guò)程生成不同的產(chǎn)品成為可能,本文就通過(guò)代碼示例為大家詳細(xì)介紹Golang生成器模式,感興趣的同學(xué)可以參考下
    2023-06-06
  • Gorm更新零值問(wèn)題解決思路與過(guò)程

    Gorm更新零值問(wèn)題解決思路與過(guò)程

    這篇文章主要介紹了Gorm更新零值問(wèn)題解決思路與過(guò)程,總的來(lái)說(shuō)這并不是一道難題,那為什么要拿出這道題介紹?拿出這道題真正想要傳達(dá)的是解題的思路,以及不斷優(yōu)化探尋最優(yōu)解的過(guò)程。希望通過(guò)這道題能給你帶來(lái)一種解題優(yōu)化的思路
    2023-01-01
  • Go錯(cuò)誤處理之panic函數(shù)和recover函數(shù)使用及捕獲異常方法

    Go錯(cuò)誤處理之panic函數(shù)和recover函數(shù)使用及捕獲異常方法

    這篇文章主要介紹了Go錯(cuò)誤處理之panic函數(shù)使用及捕獲,本篇探討了如何使用 panic 和 recover 來(lái)處理 Go 語(yǔ)言中的異常,需要的朋友可以參考下
    2023-03-03
  • Go 加密解密算法小結(jié)

    Go 加密解密算法小結(jié)

    加密解密在實(shí)際開(kāi)發(fā)中應(yīng)用比較廣泛,常見(jiàn)的加解密分為三種,本文就詳細(xì)的介紹一下Go 加密解密算法,具有一定的參考價(jià)值,感興趣的可以了解一下
    2022-01-01
  • 使用dep 配置golang 開(kāi)發(fā)環(huán)境的操作方法

    使用dep 配置golang 開(kāi)發(fā)環(huán)境的操作方法

    下面小編就為大家?guī)?lái)一篇使用dep 配置golang 開(kāi)發(fā)環(huán)境的操作方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-09-09
  • golang實(shí)現(xiàn)整型和字節(jié)數(shù)組之間的轉(zhuǎn)換操作

    golang實(shí)現(xiàn)整型和字節(jié)數(shù)組之間的轉(zhuǎn)換操作

    這篇文章主要介紹了golang實(shí)現(xiàn)整型和字節(jié)數(shù)組之間的轉(zhuǎn)換操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • Go語(yǔ)言定時(shí)任務(wù)cron的設(shè)計(jì)與使用

    Go語(yǔ)言定時(shí)任務(wù)cron的設(shè)計(jì)與使用

    這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言中定時(shí)任務(wù)cron的設(shè)計(jì)與使用,文中的示例代碼講解詳細(xì),對(duì)我們深入掌握Go語(yǔ)言有一定的幫助,需要的可以參考下
    2023-11-11
  • Golang 高效排序數(shù)據(jù)詳情

    Golang 高效排序數(shù)據(jù)詳情

    本文我們介紹了怎么使用 Golang 語(yǔ)言標(biāo)準(zhǔn)庫(kù) sort 包排序數(shù)據(jù),需要注意的是,除了本文使用的類型之外,其它任意類型只要實(shí)現(xiàn) sort.Interface 的三個(gè)方法,都可以調(diào)用 sort.Sort() 函數(shù)排序數(shù)據(jù)。
    2021-11-11
  • go原生庫(kù)的中bytes.Buffer用法

    go原生庫(kù)的中bytes.Buffer用法

    這篇文章主要介紹了go原生庫(kù)的中bytes.Buffer用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • Go語(yǔ)言net包RPC遠(yuǎn)程調(diào)用三種方式http與json-rpc及tcp

    Go語(yǔ)言net包RPC遠(yuǎn)程調(diào)用三種方式http與json-rpc及tcp

    這篇文章主要為大家介紹了Go語(yǔ)言net包RPC遠(yuǎn)程調(diào)用三種方式分別使用http與json-rpc及tcp的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2021-11-11

最新評(píng)論