構建 Go 命令行程序工具鏈

偷懶的故事

今天的推薦需要從一個偷懶的故事說起 ···

話說不久前,需要輸出一個按發佈時間排序的酷 Go 推薦的歷史文章列表,類似於這樣:

於是乎,想着從 GoCN 上一篇一篇 copy 文章的標題吧,5 篇下來,手眼已經不協調了,此時此刻纔想起自己貌似是個碼農,此情此景那必須 coding 一段,讓代碼來輸出這個列表。

思路很簡單,看一下 GoCN 文章列表的 API,再看下鑑權方式(看 header 是通過 cookie),然後就可以 coding 了,通過 API 拉取文章數據,提取需要的字段再組裝成 MD 輸出就完事了!

當準備一個 main.go 搞定的時候,本着對 coding 的敬畏,簡單的思考了一下程序的可讀性、拓展性,決定還是構建一個比較完整的命令行程序,如下:

主角登場

那麼要實現上圖的命令行提示,程序參數解析等,不借助於第三方包當然也能實現。但,前人種好的大樹爲哈不乘涼呢,在業界對於命令行工具的開發已經有比較成熟的實現,下面清楚本文的主角:

下面結合實際項目來看如何使用這三位大哥來構建 Go 命令行程序。

先簡單看下項目結構:

.
├── cmd
│   ├── pull.go
│   └── root.go
├── config.yaml
├── file
│   └── cool-go.md
├── go.mod
├── go.sum
├── gocn-cli
└── main.go

然後引入上面所推薦的三個庫:

go get -u github.com/spf13/cobra@latest

go get -u github.com/spf13/viper

go get -u github.com/spf13/pflag

Coding

package cmd

import (
    "fmt"
    "github.com/spf13/cobra"
    "github.com/spf13/viper"
    "os"
)

var (
    cfgFile string
)

func init() {
    cobra.OnInitialize(initConfig)
    
    // 解析配置文件參數
    rootCmd.PersistentFlags().StringVar(&cfgFile, "config""""config file (default is ./config.yaml)")
}

// initConfig viper加載配置文件
func initConfig() {
    if cfgFile != "" {
        viper.SetConfigFile(cfgFile)
    } else {
        viper.AddConfigPath(".")
        viper.SetConfigName("config")
    }
    
    if err := viper.ReadInConfig(); err != nil {
        fmt.Println("Can't read config file:", err)
        os.Exit(1)
    }
}

// 創建全局rootCmd
var rootCmd = &cobra.Command{
    Use:   "gocn-cli",
    Short: "gocn-cli is a command line tool for gocn.com",
    Long:  "gocn-cli is a command line tool for gocn.com",
    Run: func(cmd *cobra.Command, args []string) {
        
    },
}

// Execute 執行命令 main調用
func Execute() {
    if err := rootCmd.Execute(); err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}
package cmd

import (
 "github.com/spf13/cobra"
 "github.com/spf13/viper"
 "os"
)

var (
 moduleName string
)

func init() {
    // 給rootCmd增加新的命令pullCmd
 rootCmd.AddCommand(pullCmd)

 pullCmd.Flags().StringVarP(&moduleName, "module""m""""module name")
}

var pullCmd = &cobra.Command{
 Use:   "pull gocn data",
 Short: "pull gocn data",
 Run: func(cmd *cobra.Command, args []string) {
  pullHandler()
 },
}

// pullHandler pullCmd 對應的處理函數
func pullHandler() {
 if moduleName == "" {
  fmt.Println("module name is required")
  os.Exit(1)
 }

 switch moduleName {
 case "cool-go":
  pullCoolGo()

 default:
  fmt.Println("module name is invalid")
  os.Exit(1)
 }
}

func pullCoolGo() {
 // TODO Write Your Code
}

結語

以上簡單介紹瞭如何使用 cobra、viper、pflag,當然它們的用法還有很多,這裏只做個簡單的入門。

可見,通過簡單的代碼就能構建出比較好的命令行程序。

參考

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/fIChLPgtzQJoLJLSIoPitw