構(gòu)建Go命令行程序工具鏈
偷懶的故事
話說不久前,需要輸出一個(gè)按發(fā)布時(shí)間排序的酷Go推薦的歷史文章列表,類似于這樣:

思路很簡單,看一下GoCN文章列表的API,再看下鑒權(quán)方式(看header是通過cookie),然后就可以coding了,通過API拉取文章數(shù)據(jù),提取需要的字段再組裝成MD輸出就完事了!
當(dāng)準(zhǔn)備一個(gè)main.go搞定的時(shí)候,本著對coding的敬畏,簡單的思考了一下程序的可讀性、拓展性,決定還是構(gòu)建一個(gè)比較完整的命令行程序,如下:

主角登場
那么要實(shí)現(xiàn)上圖的命令行提示,程序參數(shù)解析等,不借助于第三方包當(dāng)然也能實(shí)現(xiàn)。但,前人種好的大樹為哈不乘涼呢,在業(yè)界對于命令行工具的開發(fā)已經(jīng)有比較成熟的實(shí)現(xiàn),下面清楚本文的主角:
cobra:應(yīng)用命令行框架 pflag:命令行參數(shù)解析 viper:配置文件解析
先簡單看下項(xiàng)目結(jié)構(gòu):
.
├── cmd
│ ├── pull.go
│ └── root.go
├── config.yaml
├── file
│ └── cool-go.md
├── go.mod
├── go.sum
├── gocn-cli
└── main.go
然后引入上面所推薦的三個(gè)庫:
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)
// 解析配置文件參數(shù)
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)
}
}
// 創(chuàng)建全局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 執(zhí)行命令 main調(diào)用
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 對應(yīng)的處理函數(shù)
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
}
結(jié)語
參考
https://github.com/spf13/cobra https://github.com/spf13/viper https://github.com/spf13/pflag
往期推薦
想要了解Go更多內(nèi)容,歡迎掃描下方?? 關(guān)注 公眾號,回復(fù)關(guān)鍵詞 [實(shí)戰(zhàn)群] ,就有機(jī)會進(jìn)群和我們進(jìn)行交流
分享、在看與點(diǎn)贊,至少我要擁有一個(gè)叭~

評論
圖片
表情



