構建 Go 命令行程序工具鏈
偷懶的故事
今天的推薦需要從一個偷懶的故事說起 ···
話說不久前,需要輸出一個按發佈時間排序的酷 Go 推薦的歷史文章列表,類似於這樣:
於是乎,想着從 GoCN 上一篇一篇 copy 文章的標題吧,5 篇下來,手眼已經不協調了,此時此刻纔想起自己貌似是個碼農,此情此景那必須 coding 一段,讓代碼來輸出這個列表。
思路很簡單,看一下 GoCN 文章列表的 API,再看下鑑權方式(看 header 是通過 cookie),然後就可以 coding 了,通過 API 拉取文章數據,提取需要的字段再組裝成 MD 輸出就完事了!
當準備一個 main.go 搞定的時候,本着對 coding 的敬畏,簡單的思考了一下程序的可讀性、拓展性,決定還是構建一個比較完整的命令行程序,如下:
主角登場
那麼要實現上圖的命令行提示,程序參數解析等,不借助於第三方包當然也能實現。但,前人種好的大樹爲哈不乘涼呢,在業界對於命令行工具的開發已經有比較成熟的實現,下面清楚本文的主角:
-
cobra:應用命令行框架
-
pflag:命令行參數解析
-
viper:配置文件解析
下面結合實際項目來看如何使用這三位大哥來構建 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,當然它們的用法還有很多,這裏只做個簡單的入門。
可見,通過簡單的代碼就能構建出比較好的命令行程序。
參考
-
https://github.com/spf13/cobra
-
https://github.com/spf13/viper
-
https://github.com/spf13/pflag
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/fIChLPgtzQJoLJLSIoPitw