Cobra- Go的命令行生成库

概述

Cobra 是一个用来创建 CLI 命令行的 golang 库,也可以用来生成以Cobra为基础的应用和命令文件。被广泛使用在各种Go的项目中,比如Kubernetes,Docker。

概念

有这样两条命令,下面以它为参考,解释cobra的几个概念。

hugo server --port=1313
git clone URL --bare

Commands
Command 是程序的中心点,不同Commands有不同行为,一个命令可能有多个子命令。示例中server就是命令。

Flags

Flags 用来改变命令的行为,比如port就是Flag。假设命令的flag为version, 使用方式:--version,也可以配置短别名:-V。详见GNU extensions to the POSIX recommendations for command-line options

示例

工程结构

-- netrq
    |-- cmd
    |   |-- http.go
    |   |-- root.go
    |   `-- version.go
    |-- config.json
    |-- main.go
    `-- pkg
        `-- request.go

Create rootCmd

netrq/cmd/root.go

package cmd

import (
	"github.com/spf13/cobra"
	"fmt"
	"os"
	"github.com/spf13/viper"
	"github.com/mitchellh/go-homedir"
	"github.com/fsnotify/fsnotify"
)


var cfgFile string
var testPersistentFlag,testLocalFlag,author string

var rootCmd = &cobra.Command{
	Use:   "netrq",
	Short: "This tool for network request",
	Long: `This tool for network request, 
Complete documentation is available at http://document.netrq.com `,
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println(args)
		author := viper.GetString("author")
		if len(author) > 0 {
			fmt.Println("author: ", author)
		} else {
			cmd.Help()
		}
	},
}

func Execute() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}
func init() {
	cobra.OnInitialize(initConfig)

	// persistent Flag: 对本命令和所有子命令都有效,Global Flags:
	rootCmd.PersistentFlags().StringVarP(&testPersistentFlag, "tpFlag", "p", "", "persistent flag")
	rootCmd.PersistentFlags().StringVarP(&author, "author","a", "","Author name for copyright attribution")


	// local flag: 本地flag,对当前命令有效
	rootCmd.Flags().StringVarP(&testPersistentFlag, "tlFlag", "l", "", "local flag")

	viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author"))

}
func initConfig() {
	if cfgFile != "" {
		viper.SetConfigName(cfgFile)
	} else {
		home, err := homedir.Dir()
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		// home目录和当前目录查找配置文件
		viper.AddConfigPath(home)
		viper.AddConfigPath(".")

		// 注意,我在Windows上,不需要加扩展名
		viper.SetConfigName("config")

		// 监听文件变化
		viper.WatchConfig()
		viper.OnConfigChange(func(e fsnotify.Event) {
			fmt.Println("Config file changed:", e.Name)
		})
	}
	viper.AutomaticEnv()
	if err := viper.ReadInConfig(); err != nil {
		fmt.Println("Can not read config:", viper.ConfigFileUsed(),err)
	}
}

rootCmd: 根命令,是整个应用命令的入口,通过在main函数中调用rootCmd.Execute()启动;
* Use: 命令
* Short: 命令的简短描述
* Long: 命令的详细描述
* Run: 命令执行入口

函数initConfig(): 用来初始化viper配置文件位置,监听变化
函数init(): 定义flag和配置处理

rootCmd.PersistentFlags().StringVarP(&author, "author","a", "","Author name for copyright attribution")
viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author"))

把viper配置项author和flag绑定,也就是说,&author变量的值会从配置文件中读取,如果命令有flag--author,变量将使用此flag的值:

Create your main.go

文件netrq/main.go

import (
	"netrq/cmd"
)

func main() {
	cmd.Execute();
}

有这俩文件,可以编译执行:

cd netrq
go build .
./netrq -h #将列出可用命令和flag

Create additional commands

我希望执行netrq version 时输出 version : 1.0.0,也就是给netrq添加一个子命令,输出版本号;
netrq/cmd/version.go

package cmd

import "github.com/spf13/cobra"

func init() {
    // versionCmd作为rootCmd的子命令
    rootCmd.AddCommand(versionCmd)
}

const version string = "1.0.0"

var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Print the netrq version number",
	Run: func(cmd *cobra.Command, args []string) {
		println("version : ", version)
	},
}

同样,也可以添加一个http的子命令,用来发起http请求;
netrq/cmd/http.go

package cmd

import (
	"github.com/spf13/cobra"
	"netrq/pkg"
)

var url,method string

var httpCmd = &cobra.Command{
	Use:   "http",
	Short: "Short desc: invoke http request",
	Long:  `Long description: invoke http request 
		http [flags]
	`,
	Run: func(cmd *cobra.Command, args []string) {
		if len(url) == 0 {
			cmd.Help()
			return
		}
		pkg.HttpRequest(url, method)
	},
}
func init() {
	httpCmd.Flags().StringVarP(&url, "url", "U", "", "http url")
	// required 
	httpCmd.MarkFlagRequired("url")
	httpCmd.Flags().StringVarP(&method, "method", "X", "GET", "http method")

	// add
	rootCmd.AddCommand(httpCmd)
}

netrq/pkg/request.go

package pkg

import (
	"net/http"
	"log"
	"io/ioutil"
	"fmt"
)

func HttpRequest(url string, method string) {
	client := &http.Client{}
	req, err := http.NewRequest(method, url, nil)
	if err != nil {
		log.Println(err)
		return
	}
	//req.Header.Set("Content-Type", "application/json; charset=UTF-8")
	resp, err := client.Do(req)
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
		return
	}
	fmt.Println(string(body))
}

测试:

$ netrq http -X GET -U https://api.github.com/users/ThoreauZZ 
{"login":"ThoreauZZ","id":10337273,"avatar_url":"https://avatars1.githubusercontent.com/u/10337273?v=4","gravatar_id":"","url":"https://api.github.com/users/ThoreauZZ
","html_url":"https://github.com/ThoreauZZ","followers_url":"https://api.github.com/users/ThoreauZZ/followers","following_url":"https://api.github.com/users/ThoreauZZ
/following{/other_user}","gists_url":"https://api.github.com/users/ThoreauZZ/gists{/gist_id}","starred_url":"https://api.github.com/users/ThoreauZZ/starred{/owner}{/r
epo}","subscriptions_url":"https://api.github.com/users/ThoreauZZ/subscriptions","organizations_url":"https://api.github.com/users/ThoreauZZ/orgs","repos_url":"https:
//api.github.com/users/ThoreauZZ/repos","events_url":"https://api.github.com/users/ThoreauZZ/events{/privacy}","received_events_url":"https://api.github.com/users/Tho
reauZZ/received_events","type":"User","site_admin":false,"name":"thoreau","company":null,"blog":"","location":null,"email":null,"hireable":null,"bio":null,"public_rep
os":30,"public_gists":0,"followers":10,"following":4,"created_at":"2014-12-29T08:15:41Z","updated_at":"2018-03-05T09:27:29Z"}

$ netrq http -X POST -U https://api.github.com/users/ThoreauZZ 
{"message":"Not Found","documentation_url":"https://developer.github.com/v3"}

Cobra Generator

cobra除了作为一个库,还是一个生成工具.

安装

go get github.com/spf13/cobra/cobra

使用

# 生成项目init
cobra init github.com/ThoreauZZ/newApp
cd newApp/


# 添加命令add
cobra add server
cobra add config

# 自动生成的文件
tree

|-- LICENSE
|-- cmd
|   |-- config.go
|   |-- root.go
|   `-- serve.go
`-- main.go

参考文档:
cobra
viper
pflag

CONTENTS