一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

腳本之家,腳本語言編程技術及教程分享平臺!
分類導航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務器之家 - 腳本之家 - Golang - 在 Golang 中使用 Cobra 創建 CLI 應用

在 Golang 中使用 Cobra 創建 CLI 應用

2022-01-26 11:30吱吱吱 (piperck) XD Golang

這篇文章主要介紹了在 Golang 中使用 Cobra 創建 CLI 應用,來看下 Cobra 的使用,這里我們使用的 go1.13.3 版本,使用 Go Modules 來進行包管理,需要的朋友可以參考下

雖然現在我們使用的大多數軟件都是可視化的,很容易上手,但是這并不代表 CLI(命令行)應用就沒有用武之地了,特別是對于開發人員來說,還是會經常和 CLI 應用打交道。而 Golang 就非常適合用來構建 CLI 應用,下面我們就將來介紹如何在 Golang 中構建一個 CLI 應用。

對于開發人員來說平時可能就需要使用到很多 CLI 工具,比如 npm、node、go、python、docker、kubectl 等等,因為這些工具非常小巧、沒有依賴性、非常適合系統管理或者一些自動化任務等等。

我們這里選擇使用 Golang 里面非常有名的Cobra庫來進行 CLI 工具的開發。Cobra 是一個功能強大的現代化 CLI 應用程序庫,有很多知名的 Go 項目使用 Cobra 進行構建,比如:Kubernetes、Docker、Hugo 等等

概念

Cobra 是構建在命令、參數和標識符之上的:

  • Commands表示執行動作
  • Args就是執行參數
  • Flags是這些動作的標識符

基本的執行命令如下所示:

?
1
2
3
$ APPNAME Command Args --Flags
# 或者
$ APPNAME Command --Flags Args

比如我們平時使用的一些命令行工具:

?
1
2
3
4
git clone URL -bare
go get -u URL
npm install package –save
kubectl get pods -n kube-system -l app=cobra

示例

下面我們來看下 Cobra 的使用,這里我們使用的 go1.13.3 版本,使用 Go Modules 來進行包管理,如果對這部分知識點不熟悉的,可以查看前面我們的文章Go Modules 基本使用(視頻)了解。

新建一個名為my-calc的目錄作為項目目錄,然后初始化 modules:

?
1
2
3
4
$ mkdir my-calc && cd my-calc
# 如果 go modules 默認沒有開啟,需要執行 export GO111MODULE=on 開啟
$ go mod init my-calc
go: creating new go.mod: module my-calc

初始化完成后可以看到項目根目錄下面多了一個go.mod的文件,現在我們還沒有安裝cobra庫,執行下面的命令進行安裝:

?
1
2
3
# 強烈推薦配置該環境變量
$ export GOPROXY=https://goproxy.cn
$ go get -u github.com/spf13/cobra/cobra

安裝成功后,現在我們可以使用cobra init命令來初始化 CLI 應用的腳手架:

?
1
2
3
$ cobra init --pkg-name my-calc
Your Cobra applicaton is ready at
/Users/ych/devs/workspace/youdianzhishi/course/my-calc

需要注意的是新版本的 cobra 庫需要提供一個--pkg-name參數來進行初始化,也就是指定上面我們初始化的模塊名稱即可。上面的 init 命令就會創建出一個最基本的 CLI 應用項目:

?
1
2
3
4
5
6
7
8
9
10
$ tree .
.
├── LICENSE
├── cmd
│   └── root.go
├── go.mod
├── go.sum
└── main.go
 
1 directory, 5 files

其中main.go是 CLI 應用的入口,在main.go里面調用好了cmd/root.go下面的Execute函數:

?
1
2
3
4
5
6
7
8
// main.go
package main
 
import "my-calc/cmd"
 
func main() {
    cmd.Execute()
}

然后我們再來看下cmd/root.go文件。

rootCmd

root(根)命令是 CLI 工具的最基本的命令,比如對于我們前面使用的go get URL,其中go就是 root 命令,而get就是go這個根命令的子命令,而在root.go中就直接使用了 cobra 命令來初始化rootCmd結構,CLI 中的其他所有命令都將是rootCmd這個根命令的子命令了。

這里我們將cmd/root.go里面的rootCmd變量內部的注釋去掉,并在Run函數里面加上一句fmt.Println("Hello Cobra CLI")

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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")
    },
}

這個時候我們在項目根目錄下面執行如下命令進行構建:

?
1
$ go build -o my-calc

該命令會在項目根目錄下生成一個名為my-calc的二進制文件,直接執行這個二進制文件可以看到如下所示的輸出信息:

$ ./my-calc
Hello Cobra CLI

init

我們知道init函數是 Golang 中初始化包的時候第一個調用的函數。在cmd/root.go中我們可以看到init函數中調用了cobra.OnInitialize(initConfig),也就是每當執行或者調用命令的時候,它都會先執行init函數中的所有函數,然后再執行execute方法。該初始化可用于加載配置文件或用于構造函數等等,這完全依賴于我們應用的實際情況。

在初始化函數里面cobra.OnInitialize(initConfig)調用了initConfig這個函數,所有,當rootCmd的執行方法RUN: func運行的時候,rootCmd根命令就會首先運行initConfig函數,當所有的初始化函數執行完成后,才會執行rootCmdRUN: func執行函數。

我們可以在initConfig函數里面添加一些 Debug 信息:

?
1
2
3
4
func initConfig() {
    fmt.Println("I'm inside initConfig function in cmd/root.go")
    ...
}

然后同樣重新構建一次再執行:

?
1
2
3
4
$ go build -o my-calc
$ ./my-calc
I'm inside initConfig function in cmd/root.go
Hello Cobra CLI

可以看到是首先運行的是initConfig函數里面的信息,然后才是真正的執行函數里面的內容。

為了搞清楚整個 CLI 執行的流程,我們在main.go里面也添加一些 Debug 信息:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 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()
}

然后同樣重新構建一次再執行:

?
1
2
3
4
5
6
$ 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

根據上面的日志信息我們就可以了解到 CLI 命令的流程了。

init函數最后處理的就是flags了,Flags就類似于命令的標識符,我們可以把他們看成是某種條件操作,在 Cobra 中提供了兩種類型的標識符:Persistent FlagsLocal Flags

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

initConfig

該函數主要用于在 home 目錄下面設置一個名為.my-calc的配置文件,如果該文件存在則會使用這個配置文件。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// cmd/root.go
// initConfig 讀取配置文件和環境變量
func initConfig() {
    if cfgFile != "" {
        // 使用 flag 標志中傳遞的配置文件
        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")
    }
    // 讀取匹配的環境變量
    viper.AutomaticEnv()
    // 如果有配置文件,則讀取它
    if err := viper.ReadInConfig(); err == nil {
        fmt.Println("Using config file:", viper.ConfigFileUsed())
    }
}

viper是一個非常優秀的用于解決配置文件的 Golang 庫,它可以從 JSON、TOML、YAML、HCL、envfile 以及 Java properties 配置文件中讀取信息,功能非常強大,而且不僅僅是讀取配置這么簡單,了解更多相關信息可以查看 Git 倉庫相關介紹:https://github.com/spf13/viper

現在我們可以去掉前面我們添加的一些打印語句,我們已經創建了一個my-calc命令作為rootCmd命令,執行該根命令會打印Hello Cobra CLI信息,接下來為我們的 CLI 應用添加一些其他的命令。

添加數據

在項目根目錄下面創建一個名為add的命令,Cobra添加一個新的命令的方式為:cobra add <commandName>,所以我們這里直接這樣執行:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ 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

現在我們可以看到cmd/root.go文件中新增了一個add.go的文件,我們仔細觀察可以發現該文件和cmd/root.go比較類似。首先是聲明了一個名為addCmd的結構體變量,類型為*cobra.Command指針類型,*cobra.Command有一個RUN函數,帶有*cobra.Command指針和一個字符串切片參數。

然后在init函數中進行初始化,初始化后,將其添加到rootCmd根命令中rootCmd.AddCommand(addCmd),所以我們可以把addCmd看成是rootCmd的子命令。

同樣現在重新構建應用再執行:

?
1
2
3
4
5
$ go build -o my-calc
$ ./my-calc
Hello Cobra CLI
$ ./my-calc add
add called

可以看到add命令可以正常運行了,接下來我們來讓改命令支持添加一些數字,我們知道在RUN函數中是用戶字符串 slice 來作為參數的,所以要支持添加數字,我們首先需要將字符串轉換為 int 類型,返回返回計算結果。在cmd/add.go文件中添加一個名為intAdd的函數,定義如下所示:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// cmd/add.go
func intAdd(args []string) {
    var sum int
    // 循環 args 參數,循環的第一個值為 args 的索引,這里我們不需要,所以用 _ 忽略掉
    for _, ival := range args {
        // 將 string 轉換成 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函數,移除默認的打印信息,調用上面聲明的addInt函數:

?
1
2
3
4
// addCmd
Run: func(cmd *cobra.Command, args []string) {
    intAdd(args)
},

然后重新構建應用執行如下所示的命令:

?
1
2
3
4
5
6
$ go build -o my-calc
$ ./my-calc
Hello Cobra CLI
# 注意參數之間的空格
$ ./my-calc add 1 2 3
Addition of numbers [1 2 3] is 6

由于RUN函數中的args參數是一個字符串切片,所以我們可以傳遞任意數量的參數,但是確有一個缺陷,就是只能進行整數計算,不能計算小數,比如我們執行如下的計算就會直接 panic 了:

?
1
2
3
4
5
6
$ ./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)
......

因為在intAdd函數里面,我們只是將字符串轉換成了 int,而不是 float32/64 類型,所以我們可以為addCmd命令添加一個flag標識符,通過該標識符來幫助 CLI 確定它是 int 計算還是 float 計算。

cmd/add.go文件的init函數內部,我們創建一個 Bool 類型的本地標識符,命名成float,簡寫成f,默認值為 false。這個默認值是非常重要的,意思就是即使沒有在命令行中調用 flag 標識符,該標識符的值就將為 false。

?
1
2
3
4
5
// cmd/add.go
func init() {
    rootCmd.AddCommand(addCmd)
    addCmd.Flags().BoolP("float", "f", false, "Add Floating Numbers")
}

然后創建一個floatAdd的函數:

?
1
2
3
4
5
6
7
8
9
10
11
12
func floatAdd(args []string) {
    var sum float64
    for _, fval := range args {
        // 將字符串轉換成 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)
}

該函數和上面的intAdd函數幾乎是相同的,除了是將字符串轉換成 float64 類型。然后在addCmdRUN函數中,我們根據傳入的標識符來判斷到底應該是調用intAdd還是floatAdd,如果傳遞了--float或者-f標志,就將會調用floatAdd函數。

?
1
2
3
4
5
6
7
8
9
10
11
// cmd/add.go
// addCmd
Run: func(cmd *cobra.Command, args []string) {
    // 獲取 float 標識符的值,默認為 false
    fstatus, _ := cmd.Flags().GetBool("float")
    if fstatus { // 如果為 true,則調用 floatAdd 函數
        floatAdd(args)
    } else {
        intAdd(args)
    }
},

現在重新編譯構建 CLI 應用,按照如下方式執行:

?
1
2
3
4
5
6
7
$ 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

然后接下來我們在給addCmd添加一些子命令來擴展它。

添加偶數

同樣在項目根目錄下執行如下命令添加一個名為even的命令:

?
1
2
$ cobra add even
even created at /Users/ych/devs/workspace/youdianzhishi/course/my-calc

和上面一樣會在root目錄下面新增一個名為even.go的文件,修改該文件中的init函數,將rootCmd修改為addCmd,因為我們是為addCmd添加子命令:

?
1
2
3
4
// cmd/even.go
func init() {
    addCmd.AddCommand(evenCmd)
}

然后更新evenCmd結構體參數的RUN函數:

?
1
2
3
4
5
6
7
8
9
10
11
// 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)
},

首先將字符串轉換成整數,然后判斷如果是偶數才進行累加。然后重新編譯構建應用:

?
1
2
3
$ 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優勢addCmd的子命令,所以按照上面的方式調用??梢杂猛瑯拥姆绞皆偃ヌ砑右粋€奇數相加的子命令。

到這里我們就在 Golang 里面使用Cobra創建了一個簡單的 CLI 應用。本文的內容雖然比較簡單,但是是我們了解學習Cobra基礎的一個很好的入門方式,后續我們也可以嘗試添加一些更加復雜的使用案例。

Reference:

https://www.qikqiak.com/post/create-cli-app-with-cobra在 Golang 中使用 Cobra 創建 CLI 應用

https://cobra.dev cobra docs

到此這篇關于在 Golang 中使用 Cobra 創建 CLI 應用的文章就介紹到這了,更多相關Golang創建 CLI 應用內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://www.cnblogs.com/piperck/p/15762355.html

延伸 · 閱讀

精彩推薦
  • GolangGolang中Bit數組的實現方式

    Golang中Bit數組的實現方式

    這篇文章主要介紹了Golang中Bit數組的實現方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    天易獨尊11682021-06-09
  • Golanggolang的httpserver優雅重啟方法詳解

    golang的httpserver優雅重啟方法詳解

    這篇文章主要給大家介紹了關于golang的httpserver優雅重啟的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,...

    helight2992020-05-14
  • Golanggolang如何使用struct的tag屬性的詳細介紹

    golang如何使用struct的tag屬性的詳細介紹

    這篇文章主要介紹了golang如何使用struct的tag屬性的詳細介紹,從例子說起,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看...

    Go語言中文網11352020-05-21
  • Golanggo日志系統logrus顯示文件和行號的操作

    go日志系統logrus顯示文件和行號的操作

    這篇文章主要介紹了go日志系統logrus顯示文件和行號的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    SmallQinYan12302021-02-02
  • Golanggo語言制作端口掃描器

    go語言制作端口掃描器

    本文給大家分享的是使用go語言編寫的TCP端口掃描器,可以選擇IP范圍,掃描的端口,以及多線程,有需要的小伙伴可以參考下。 ...

    腳本之家3642020-04-25
  • Golanggolang 通過ssh代理連接mysql的操作

    golang 通過ssh代理連接mysql的操作

    這篇文章主要介紹了golang 通過ssh代理連接mysql的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    a165861639710342021-03-08
  • GolangGolang通脈之數據類型詳情

    Golang通脈之數據類型詳情

    這篇文章主要介紹了Golang通脈之數據類型,在編程語言中標識符就是定義的具有某種意義的詞,比如變量名、常量名、函數名等等,Go語言中標識符允許由...

    4272021-11-24
  • Golanggolang json.Marshal 特殊html字符被轉義的解決方法

    golang json.Marshal 特殊html字符被轉義的解決方法

    今天小編就為大家分享一篇golang json.Marshal 特殊html字符被轉義的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧 ...

    李浩的life12792020-05-27
主站蜘蛛池模板: 欧美第一视频 | 性夜影院爽黄A爽免费动漫 性色欲情网站IWWW九文堂 | 爱情岛论坛亚洲永久入口口 | 精品国产一区二区三区久久久蜜臀 | 欧美3p大片在线观看完整版 | 好男人资源免费播放在线观看 | 久久久精品3d动漫一区二区三区 | gogo人体模特啪啪季玥图片 | 6080欧美一区二区三区四区 | 日一日操一操 | 婷婷日韩| 9久热这里只有精品免费 | 18美女光胸光屁屁洗澡 | 变态 调教 视频 国产九色 | 东北美女野外bbwbbw免费 | 亚洲精品AV无码喷奶水糖心 | 亚洲福利电影一区二区? | cosplay 极品videos| ai换脸造梦jennie | 日韩毛片免费 | 高清男的插曲女的 欢迎你老狼 | 万域之王在线观看 | 精品久久久久久亚洲精品 | 成人四虎| 美女脱了内裤打开腿让人桶网站o | 狠狠做五月深爱婷婷天天综合 | 国产大片51精品免费观看 | 动漫人物差差插曲漫画 | 99在线资源| 亚洲第一在线播放 | 动漫美女被吸乳羞羞小说 | 亚拍一区| 动漫美丽妇人1~2在线看 | 亚洲激情欧美 | 特级淫片大乳女子高清视频 | 国产男女爱视频在线观看 | 国产精品99在线观看 | 视频在线观看一区二区 | 成人网18免费网站 | 日本videos有奶水的hd | 欧美成人精品福利在线视频 |