Cobra 簡介
Cobra 是一個流行的 Go
語言庫,用於建立強大且靈活的命令列應用程式。它由 spf13
開發,設計用於與 Go 生態系統中的其他流行庫(如 Viper
配置庫)無縫整合。Cobra
支援多級命令結構,允許定義根命令和任意數量的子命令,還可以輕鬆處理全域性和本地標誌。它自動生成幫助和使用資訊,並支援 Bash
、Zsh
、Fish
和 PowerShell
的命令補全。此外,Cobra
能夠生成 Markdown
格式的文件,使文件維護更加便捷。透過與 Viper
整合,Cobra
能處理配置檔案和環境變數,為開發者提供了強大的工具集,使建立複雜的 Client
工具變得簡單高效。Cobra
廣泛應用於各種 Go 專案中,提升了 Client
應用的開發體驗和維護效率。
下面是 Cobra
主要的功能:
- 支援多級命令結構:輕鬆定義根命令和任意數量的子命令,組織複雜的命令列應用。
- 處理全域性和本地標誌:解析和處理命令列引數,支援全域性標誌和區域性標誌。
- 自動生成幫助和使用資訊:根據命令和標誌自動生成詳細的幫助和使用資訊。
- 支援命令補全:提供 Bash、Zsh、Fish 和 PowerShell 的命令補全指令碼,提升使用者體驗。
- 生成 Markdown 格式的文件:自動生成命令列工具的 Markdown 格式文件,方便文件維護。
Cobra 的使用非常廣泛,特別是在雲原生和 DevOps 工具中。下面是使用 Cobra
構建 CLI
工具的知名工具:
- Kubernetes (k8s):這個流行的容器編排平臺的命令列工具 kubectl 就是使用 Cobra 構建的。
- Docker:Docker CLI 也是基於 Cobra 開發的。
- Hugo:這個流行的靜態網站生成器使用 Cobra 來構建其命令列介面。
- GitHub CLI:GitHub 的官方命令列工具 gh 也使用了 Cobra。
- CoreDNS:這個靈活可擴充套件的 DNS 伺服器使用 Cobra 來管理其命令列介面。
下面讓我們一起進入正題,Cobra
上手教程。
Cobra 上手教程
第一步我們要建立一個專案,然後為專案安裝 Cobra
:
go get -u github.com/spf13/cobra@latest
我們在 main.go
中新增如下程式碼來建立第一個命令列工具:
package main
import (
"fmt"
"github.com/spf13/cobra" "log")
func main() {
funCmd := &cobra.Command{
Use: "fun",
Short: "Demo of Cobra,列印 FunTester !!!",
Long: "Demo of Cobra,列印 FunTester !!!,使用Cobra構建命令列工具,實現簡單的命令列功能",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Hello, FunTester!")
},
}
if err := funCmd.Execute(); err != nil {
log.Fatal(err)
}
}
這是引數的解釋:
- Use: 命令的使用方法,通常是命令的名稱。
- Short: 簡短描述,在幫助資訊中顯示。
- Long: 詳細描述,在幫助資訊中顯示。
- Run: 命令執行時的函式。
我們本地編譯執行 ./funtester
,控制檯就會列印
Hello, FunTester!
接收引數
在 Cobra
中,處理命令列引數是構建命令列應用的核心部分。Cobra
提供了多種方式來接收和處理引數,包括標誌(flags
)和位置引數(arguments
)。
flags 引數
標誌是用於提供額外資訊的引數,通常以 --flag=value 或 -f value 形式出現。Cobra 支援全域性標誌和本地標誌,標誌可以是布林型、整數型、浮點型或字串型。
我們可以在原來的程式中,建立 funCmd
之後新增這麼一段程式碼:
// 新增命令列引數,並設定預設值,使用StringVarP方法,第一個引數是指標,第二個引數是命令列引數名稱,第三個引數是命令列引數的簡寫,第四個引數是預設值,第五個引數是命令列引數的描述
funCmd.Flags().StringP("name", "n", "FunTester", "姓名,用於列印")
然後我們在引數 Run
引數中這麼處理:
Run: func(cmd *cobra.Command, args []string) {
name, err := cmd.Flags().GetString("name")
if err == nil {
fmt.Printf("Hello, %s ! \n", name)
} else {
fmt.Println(fmt.Sprintln(err))
}
},
這樣我們就可以接收引數了,使用方法如下:
╰─⠠⠵ ./funtester --name FunTester001
Hello, FunTester001 !
╰─⠠⠵ ./funtester -n FunTester002
Hello, FunTester002 !
同樣地,Cobra
還提供了整型和浮點型資料接收和獲取方法,這裡就不一一展示了。完整程式碼如下:
package main
import (
"fmt"
"github.com/spf13/cobra" "log")
func main() {
funCmd := &cobra.Command{
Use: "fun",
Short: "Demo of Cobra,列印 FunTester !!!",
Long: "Demo of Cobra,列印 FunTester !!!,使用Cobra構建命令列工具,實現簡單的命令列功能",
Run: func(cmd *cobra.Command, args []string) {
name, err := cmd.Flags().GetString("name")
if err == nil {
fmt.Printf("Hello, %s age: %s ! \n", name, age)
} else {
fmt.Println(fmt.Sprintln(err))
}
},
}
// 新增命令列引數,並設定預設值,使用StringVarP方法,第一個引數是指標,第二個引數是命令列引數名稱,第三個引數是命令列引數的簡寫,第四個引數是預設值,第五個引數是命令列引數的描述
funCmd.Flags().StringP("name", "n", "FunTester", "姓名,用於列印")
if err := funCmd.Execute(); err != nil {
log.Fatal(err)
}
}
如果我們想直接使用當前的屬性或者物件接收引數值,我們可以用下面的方法:
package main
import (
"fmt"
"github.com/spf13/cobra" "log")
func main() {
var name string
funCmd := &cobra.Command{
Use: "fun",
Short: "Demo of Cobra,列印 FunTester !!!",
Long: "Demo of Cobra,列印 FunTester !!!,使用Cobra構建命令列工具,實現簡單的命令列功能",
Args: cobra.ExactArgs(1), // 限制引數個數,只能有一個引數,否則報錯
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Hello, %s ! \n", name)
},
}
// 新增命令列引數,並設定預設值,和提示資訊,以及引數的簡寫,和引數的使用說明
funCmd.Flags().StringVarP(&name, "name", "n", "FunTester", "姓名,用於列印")
if err := funCmd.Execute(); err != nil {
log.Fatal(err)
}
}
如果我們定義一些必傳引數,可以用下面的語法:
funCmd.MarkFlagRequired("name")
位置引數
位置引數是命令列中緊跟在命令後面的引數,它們不以 --flag 的形式出現。位置引數可以是必需的,也可以是可選的。
我們在上一版程式碼的基礎上新增新的程式碼,增加了 Args
引數控制位置引數的個數,然後在 Run
引數中處理位置引數。完整程式碼如下:
package main
import (
"fmt"
"github.com/spf13/cobra" "log")
func main() {
funCmd := &cobra.Command{
Use: "fun",
Short: "Demo of Cobra,列印 FunTester !!!",
Long: "Demo of Cobra,列印 FunTester !!!,使用Cobra構建命令列工具,實現簡單的命令列功能",
Args: cobra.ExactArgs(1), // 限制引數個數,只能有一個引數,否則報錯
Run: func(cmd *cobra.Command, args []string) {
age := args[0]
name, err := cmd.Flags().GetString("name")
if err == nil {
fmt.Printf("Hello, %s age: %s ! \n", name, age)
} else {
fmt.Println(fmt.Sprintln(err))
}
},
}
// 新增命令列引數,並設定預設值,使用StringVarP方法,第一個引數是指標,第二個引數是命令列引數名稱,第三個引數是命令列引數的簡寫,第四個引數是預設值,第五個引數是命令列引數的描述
funCmd.Flags().StringP("name", "n", "FunTester", "姓名,用於列印")
if err := funCmd.Execute(); err != nil {
log.Fatal(err)
}
}
下面我們來編譯執行看看。
╰─⠠⠵ ./funtester 123 --name FunTester002
Hello, FunTester002 age: 123 !
╰─⠠⠵ ./funtester --name FunTester002 3242
Hello, FunTester002 age: 3242 !
╰─⠠⠵ ./funtester --name FunTester002
Error: accepts 1 arg(s), received 0
Usage:
fun [flags]
Flags:
-h, --help help for fun
-n, --name string 姓名,用於列印 (default "FunTester")
2024/07/26 15:21:23 accepts 1 arg(s), received 0
可以看出,位置引數無論在前還是在後,都不影響,但是如果缺少,就會報錯。不得不說,Cobra
相容性還不錯。
如果我們位置引數數量不固定,對於引數的要求也比較多,可以參考 cobra.PositionalArgs
來解決,這裡定義:
type PositionalArgs func(cmd *Command, args []string) error
下面是 Cobra
提供的幾種實現型別:
在 Cobra 中,PositionalArgs
是一個用於定義和驗證命令列位置引數的型別。它允許你指定命令的引數數量和驗證規則。以下是幾種常見的 PositionalArgs
驗證函式以及如何使用它們的示例:
常見的 PositionalArgs
驗證函式
-
cobra.NoArgs
:不接受任何位置引數。 -
cobra.ArbitraryArgs
:接受任意數量的位置引數。 -
cobra.ExactArgs(n int)
:接受確切數量的引數。 -
cobra.MinimumNArgs(n int)
:接受至少 n 個引數。 -
cobra.MaximumNArgs(n int)
:接受最多 n 個引數. -
cobra.RangeArgs(min, max int)
:接受引數數量在指定範圍內(包括邊界)。
示例程式碼
以下是一些使用 PositionalArgs
驗證函式的示例:
-
cobra.NoArgs
: 不接受任何位置引數。 -
cobra.ArbitraryArgs
: 接受任意數量的位置引數。 -
cobra.ExactArgs(n int)
: 接受確切數量的引數。 -
cobra.MinimumNArgs(n int)
: 接受至少 n 個引數。 -
cobra.MaximumNArgs(n int)
: 接受最多 n 個引數。 -
cobra.RangeArgs(min, max int)
: 接受引數數量在指定範圍內。
這些驗證函式使得處理和驗證命令列引數變得更加簡單和直觀。根據應用的需求選擇合適的 PositionalArgs
函式,可以確保使用者提供正確數量和型別的引數,從而提高應用的可靠性和使用者體驗。
全域性共享標誌
在 Cobra
中,PersistentFlags
是用於在所有命令(包括子命令)中共享的標誌。PersistentFlags
通常用於定義全域性設定或配置引數,這些引數在所有命令中都可以使用。
使用方法如下:
// 為命令新增引數,預設值為FunTester
funCmd.PersistentFlags().StringP("name", "n", "FunTester", "姓名,用於列印")
這個方法通常在構建子命令的時候用到,在父命令新增標誌,所有的子命令也同時會擁有同樣的標識。
子命令
在 Cobra 中,子命令(Subcommands)是命令列工具的一部分,使得工具能夠包含多個操作或功能,每個子命令都可以有自己的標誌和引數。子命令讓命令列工具更加模組化和靈活。
下面是一個簡單的例子:
package main
import (
"fmt"
"github.com/spf13/cobra" "log")
func main() {
var name string
funCmd := &cobra.Command{
Use: "fun",
Short: "Demo of Cobra,列印 FunTester !!!",
Long: "Demo of Cobra,列印 FunTester !!!,使用Cobra構建命令列工具,實現簡單的命令列功能",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Hello, %s ! \n", name)
},
}
// 為命令新增引數,預設值為FunTester
funCmd.PersistentFlags().StringVarP(&name, "name", "n", "FunTester", "姓名,用於列印")
funCmd.MarkFlagRequired("name") // 標記為必須引數
sub := &cobra.Command{
Use: "sub",
Short: "sub command",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("sub command print: " + name)
},
}
funCmd.AddCommand(sub)
if err := funCmd.Execute(); err != nil {
log.Fatal(err)
}
}
使用方法如下:
╰─⠠⠵ ./funtester sub --name FunTester002 32432
sub command print: FunTester002
其他
Cobra
其他一些高階功能,暫時還用不到,有興趣的可以自行研究研究。PS:要生成 Markdown 文件並將其轉換為 man 頁面格式,可以使用 github.com/cpuguy83/go-md2man/v2/md2man
包。也可以使用 --help
來獲取自動生成的提示資訊。
FunTester 原創精華
- 服務端功能測試
- 效能測試專題
- Java、Groovy、Go、Python
- 單元&白盒&工具合集
- 測試方案&BUG&爬蟲&UI
- 測試理論雞湯
- 社群風采&影片合集