教你用Cobra開發相似docker的命令行

前言

Cobra是一個強大的用來構建命令行程序的庫,許多流行的Go項目都是用它來構建的,好比Kubernetes、Docker、etcd、Istio、Github CLI等等。git

接下來,演示開發一個咱們本身的命令行程序chenqionghe,模仿一下docker命令行,預期功能以下github

# 查看幫助
chenqiong -h 
# 查看版本,相似docker version
chenqionghe version 
# 查看hello命令幫助,相似docker ps -h
chenqionghe hello -h 
# 使用hello命令,相似docker run --name app --volume /app/data
chenqionghe hello --name light-weight-baby --author gym

Cobra基於三個基本概念docker

  • commands(行爲)
  • arguments(位置參數)
  • flags(命令行選項)

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

# server是一個command,--port=1313是一個命令行選項
hugo server --port=1313
#  clone 是 commands,URL 是 arguments,brae是命令行選項
git clone URL --bare

1、安裝

go get -u github.com/spf13/cobra/cobra
go install github.com/spf13/cobra/cobra

2、初始化應用

初始化項目

這裏個人應用名叫chenqiongheapp

go mod init chenqionghe

建立入口文件cmd/root.go

建立文件夾cmd,並建立文件cmd/root.go,這是用來放全部的命令的基本文件ide

package cmd
import (
	"fmt"
	"github.com/spf13/cobra"
	"os"
)
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")
	},
}
func Execute() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

建立主程序main.go

package main

import "chenqionghe/cmd"

func main() {
	cmd.Execute()
}

運行一下main.go能夠看到生效了
函數

3、如何自定義命令

建立hello子命令

cobra add hello

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

咱們把沒用的信息幹掉,精簡後以下命令行

package cmd

import (
	"fmt"
	"github.com/spf13/cobra"
)
var helloCmd = &cobra.Command{
	Use:   "hello",
	Short: "hello命令簡介",
	Long:  `hello命令詳細介紹`,
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println("hello called")
	},
	TraverseChildren: true,
}

func init() {
	rootCmd.AddCommand(helloCmd)
}

直接運行看看3d

go run main.go hello

建立version子命令

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

cobra add version

修改一下Run方法

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

運行以下

4、如何設置flag選項

flag選項按做用範圍分爲persistent和local兩類

全局選項

persistent是全局選項,對應的方法爲PersistentFlags,能夠分配給命令和命令下的全部子命令,上面的rootCmd和helloCmd都是能夠調用flag
例如,添加一個-v選項

func init() {
	var Verbose bool
	rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "全局版本")
}

運行,能夠看到生效了

本地選項

local爲本地選項,對應方法爲Flags,只對指定的Command生效,咱們往hello命令的init裏邊添加一個本地flag

func init() {
	rootCmd.AddCommand(helloCmd)
	//本地flag
	var Source string
	helloCmd.Flags().StringVarP(&Source, "source", "s", "", "讀取文件路徑")
}

運行以下

設置必填

咱們在init函數添加如下代碼

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

運行以下,必須填寫name參數才能夠運行

綁定配置

添加一個initConfig方法

func initConfig() {
	viper.AddConfigPath("./")
	viper.AddConfigPath("./conf")
	viper.SetConfigName("config")
	viper.SetConfigType("yaml")
	viper.AutomaticEnv()
	if err := viper.ReadInConfig(); err != nil {
		fmt.Println("Error:", err)
		os.Exit(1)
	}
}

在init中調用

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

這將把viper配置和flag綁定,若是用戶不設置-author選項,將從配置中查找

5、如何設置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),
}

運行輸出

6、如何使用參數

咱們能夠看到核心的方法,其實就是cobra.Command中的Run參數,指定了func(cmd *cobra.Command, args []string)類型的回調
表明咱們能夠直接使用cmd和args來編寫咱們的程序

獲取flag參數

咱們能夠直接使用cmd的flag方法獲取傳遞的flag

var helloCmd = &cobra.Command{
	Use:   "hello",
	Short: "hello命令簡介",
	Long:  `hello命令詳細介紹`,
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println(cmd.Flag("author").Value)
		fmt.Println(cmd.Flag("name").Value)
	},
}

運行以下

獲取args參數

var helloCmd = &cobra.Command{
	Use:   "hello",
	Short: "hello命令簡介",
	Long:  `hello命令詳細介紹`,
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println(args)
	},
	TraverseChildren: true,
}

調用以下,能夠看到已經取出了全部的args參數

7、如何設置鉤子

cobra提供了不少鉤子方法,可按運行順序排列以下

  • PersistentPreRun
  • PreRun
  • Run
  • PostRun
  • PersistentPostRun
    使用示例
var helloCmd = &cobra.Command{
	Use:   "hello",
	Short: "hello命令簡介",
	Long:  `hello命令詳細介紹`,
	//Args: cobra.MinimumNArgs(1),
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		fmt.Printf("Inside rootCmd PersistentPreRun with args: %v\n", args)
	},
	PreRun: func(cmd *cobra.Command, args []string) {
		fmt.Printf("Inside rootCmd PreRun with args: %v\n", args)
	},
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Printf("Run with args: %v\n", args)
	},
	PostRun: func(cmd *cobra.Command, args []string) {
		fmt.Printf("Inside rootCmd PostRun with args: %v\n", args)
	},
	PersistentPostRun: func(cmd *cobra.Command, args []string) {
		fmt.Printf("Inside rootCmd PersistentPostRun with args: %v\n", args)
	},
}

運行以下

總結

到這裏,咱們就已經學會了若是設置子命令、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!

相關文章
相關標籤/搜索