其他
构建Go命令行程序工具链
偷懒的故事
话说不久前,需要输出一个按发布时间排序的酷Go推荐的历史文章列表,类似于这样:
思路很简单,看一下GoCN文章列表的API,再看下鉴权方式(看header是通过cookie),然后就可以coding了,通过API拉取文章数据,提取需要的字段再组装成MD输出就完事了!
当准备一个main.go搞定的时候,本着对coding的敬畏,简单的思考了一下程序的可读性、拓展性,决定还是构建一个比较完整的命令行程序,如下:
主角登场
那么要实现上图的命令行提示,程序参数解析等,不借助于第三方包当然也能实现。但,前人种好的大树为哈不乘凉呢,在业界对于命令行工具的开发已经有比较成熟的实现,下面清楚本文的主角:
cobra:应用命令行框架 pflag:命令行参数解析 viper:配置文件解析
先简单看下项目结构:
.
├── 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
}
结语
参考
https://github.com/spf13/cobra https://github.com/spf13/viper https://github.com/spf13/pflag
往期推荐
这不会又是一个Go的BUG吧?
想要了解Go更多内容,欢迎扫描下方👇 关注 公众号,回复关键词 [实战群] ,就有机会进群和我们进行交流
分享、在看与点赞,至少我要拥有一个叭~