首頁>技術>

前言

Cobra是一個強大的用來構建命令列程式的庫,許多流行的Go專案都是用它來構建的,比如Kubernetes、Docker、etcd、Istio、Github CLI等等。

接下來,演示開發一個我們自己的命令列程式chenqionghe,模仿一下docker命令列,預期功能如下

# 檢視幫助chenqiong -h # 檢視版本,類似docker versionchenqionghe version # 檢視hello命令幫助,類似docker ps -hchenqionghe hello -h # 使用hello命令,類似docker run --name app --volume /app/datachenqionghe hello --name light-weight-baby --author gym

Cobra基於三個基本概念

commands(行為)arguments(位置引數)flags(命令列選項)

使用基本模式是APPNAME VERB NOUN --ADJECTIVE或APPNAME COMMAND ARG --FLAG,例如

# server是一個command,--port=1313是一個命令列選項hugo server --port=1313# clone 是 commands,URL 是 arguments,brae是命令列選項git clone URL --bare一、安裝go get -u github.com/spf13/cobra/cobrago install github.com/spf13/cobra/cobra二、初始化應用gomod初始化

這裡我的應用名叫chenqionghe

go mod init chenqionghe建立入口檔案cmd/root.go

建立資料夾cmd,並建立檔案cmd/root.go,這是用來放所有的命令的基本檔案

package cmdimport (\t"fmt"\t"github.com/spf13/cobra"\t"os")var rootCmd = &cobra.Command{\tUse: "chenqionghe",\tShort: "getting muscle is not easy",\tLong: `let's do it, yeah buddy light weight baby!`,\tRun: func(cmd *cobra.Command, args []string) {\t\tfmt.Println("hello chenqionghe")\t},}func Execute() {\tif err := rootCmd.Execute(); err != nil {\t\tfmt.Println(err)\t\tos.Exit(1)\t}}建立主程式main.gopackage mainimport "chenqionghe/cmd"func main() {\tcmd.Execute()}

執行一下main.go可以看到生效了

三、生成Command建立hello子命令cobra add hello

會在cmd下生成一個hello.cmd的命令,生成的命令是長下面這樣的,核心是呼叫了AddCommand方法

我們把沒用的資訊幹掉,精簡後如下

package cmdimport (\t"fmt"\t"github.com/spf13/cobra")var helloCmd = &cobra.Command{\tUse: "hello",\tShort: "hello命令簡介",\tLong: `hello命令詳細介紹`,\tRun: func(cmd *cobra.Command, args []string) {\t\tfmt.Println("hello called")\t},\tTraverseChildren: true,}func init() {\trootCmd.AddCommand(helloCmd)}

直接執行看看

建立version子命令

同理,我們再建立一個version命令

cobra add version

修改一下Run方法

Run: func(cmd *cobra.Command, args []string) { fmt.Println("chenqionghe version v0.0.1")},

執行如下

四、如何設定flag選項

flag選項按作用範圍分為persistent和local兩類

全域性選項

persistent是全域性選項,對應的方法為PersistentFlags,可以分配給命令和命令下的所有子命令,上面的rootCmd和helloCmd都是可以呼叫flag例如,新增一個-v選項

func init() {\tvar Verbose bool\trootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "全域性版本")}

執行,可以看到生效了

本地選項

local為本地選項,對應方法為Flag,只在針對指定的Command生效,我們往hello命令的init裡邊新增一個本地flag

func init() {\trootCmd.AddCommand(helloCmd)\t//本地flag\tvar Source string\thelloCmd.Flags().StringVarP(&Source, "source", "s", "", "讀取檔案路徑")}

執行如下

設定必填

我們在init函式新增以下程式碼

rootCmd.Flags().StringVarP(&Name, "name", "n", "", "你的名字")rootCmd.MarkFlagRequired("name")

執行如下,必須填寫name引數才可以執行

繫結配置

新增一個initConfig方法

func initConfig() {\tviper.AddConfigPath("./")\tviper.AddConfigPath("./conf")\tviper.SetConfigName("config")\tviper.SetConfigType("yaml")\tviper.AutomaticEnv()\tif err := viper.ReadInConfig(); err != nil {\t\tfmt.Println("Error:", err)\t\tos.Exit(1)\t}}

在init中呼叫

cobra.OnInitialize(initConfig) //這會執行每個子命令之前執行rootCmd.PersistentFlags().StringVar(&Author, "author", "defaultAuthor", "作者名")viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author"))

這將把viper配置和flag繫結,如果使用者不設定-author選項,將從配置中查詢

五、如何設定arguments

cobra預設提供了一些驗證方法

NoArgs: 如果包含任何位置引數,命令報錯ArbitraryArgs: 命令接受任何引數OnlyValidArgs: 如果有位置引數不在ValidArgs中,命令報錯MinimumArgs(init): 如果引數數目少於N個後,命令列報錯MaximumArgs(init): 如果引數數目多餘N個後,命令列報錯ExactArgs(init): 如果引數數目不是N個話,命令列報錯RangeArgs(min, max): 如果引數數目不在範圍(min, max)中,命令列報錯使用示例

往Command中新增引數Args,我們規定引數不能少於5個,如下

var rootCmd = &cobra.Command{   Use:   "chenqionghe",   Short: "getting muscle is not easy",   Long:  `let's do it, yeah buddy light weight baby!`,   Run: func(cmd *cobra.Command, args []string) {      fmt.Println("hello chenqionghe")   },   Args: cobra.MinimumNArgs(5),}

執行輸出

六、如何使用引數

我們可以看到核心的方法,其實就是cobra.Command中的Run引數,指定了func(cmd *cobra.Command, args []string)型別的回撥代表我們可以直接使用cmd和args來編寫我們的程式

獲取flag引數

我們可以直接使用cmd的flag方法獲取傳遞的flag

var helloCmd = &cobra.Command{\tUse:   "hello",\tShort: "hello命令簡介",\tLong:  `hello命令詳細介紹`,\tRun: func(cmd *cobra.Command, args []string) {\t\tfmt.Println(cmd.Flag("author").Value)\t\tfmt.Println(cmd.Flag("name").Value)\t},}

執行如下

獲取args引數
var helloCmd = &cobra.Command{\tUse:   "hello",\tShort: "hello命令簡介",\tLong:  `hello命令詳細介紹`,\tRun: func(cmd *cobra.Command, args []string) {\t\tfmt.Println(args)\t},\tTraverseChildren: true,}

呼叫如下,可以看到已經取出了所有的args引數

七、如何設定鉤子

cobra提供了很多鉤子方法,可按執行順序排列如下

PersistentPreRunPreRunRunPostRunPersistentPostRun使用示例
var helloCmd = &cobra.Command{\tUse:   "hello",\tShort: "hello命令簡介",\tLong:  `hello命令詳細介紹`,\t//Args: cobra.MinimumNArgs(1),\tPersistentPreRun: func(cmd *cobra.Command, args []string) {\t\tfmt.Printf("Inside rootCmd PersistentPreRun with args: %v\\n", args)\t},\tPreRun: func(cmd *cobra.Command, args []string) {\t\tfmt.Printf("Inside rootCmd PreRun with args: %v\\n", args)\t},\tRun: func(cmd *cobra.Command, args []string) {\t\tfmt.Printf("Run with args: %v\\n", args)\t},\tPostRun: func(cmd *cobra.Command, args []string) {\t\tfmt.Printf("Inside rootCmd PostRun with args: %v\\n", args)\t},\tPersistentPostRun: func(cmd *cobra.Command, args []string) {\t\tfmt.Printf("Inside rootCmd PersistentPostRun with args: %v\\n", args)\t},}

執行如下

總結

到這裡,我們就已經學會了如果設定子命令、flag引數、arguments引數以及編寫方法使用這些引數,還有最後一步,就是編譯出我們的二進位制程式,驗證一下我們之前的需求

編譯
go build -o chenqionghe

如下,已經生成二進位制檔案chenqionghe

執行命令./chenqionghe -h # 檢視幫助./chenqionghe version # 檢視版本,類似docker version./chenqionghe hello -h # 檢視hello命令幫助,類似docker ps -h./chenqionghe hello --name light-weight-baby --author gym # 使用hello命令,類似docker run --name app --volume /app/data

可以看到,完美的實現了預期需求,就是這麼簡單,light weight baby!

最後

多說一句,很多人學Python過程中會遇到各種煩惱問題,沒有人解答容易放棄。小編是一名python開發工程師,這裡有我自己整理了一套最新的python系統學習教程,包括從基礎的python指令碼到web開發、爬蟲、資料分析、資料視覺化、機器學習等。想要這些資料的可以關注小編,並在後臺私信小編:“01”即可領取。

最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 企業級SpringBoot應用多個子專案配置檔案規劃、多環境支援(一)