参考资料:
-
https://github.com/spf13/cobra -
https://github.com/spf13/viper -
https://github.com/spf13/cobra/blob/master/user_guide.md
作者spf13有两个明星项目—cobra & viper (眼睛蛇与蝮蛇)
能够帮助我们的go项目添加cmd应用以及读取初始的配置文件,并且两者还可以高效配合使用。
本文就记录了一下这两个项目的基本使用方式(也是个人使用的记录),方便快速上手,如果想更详细了解的话可以去官网查看详细的文档.
一、Cobra
Cobra是一个库,提供了一个简单的界面,可以创建类似于git和go工具的强大的现代CLI界面。 Cobra也是一个应用程序,它可以生成应用程序脚手架来快速开发基于Cobra的应用程序。
1.1 安装
go get -u github.com/spf13/cobra
1.2 使用
1.2.1 架构
项目一般架构:
- cmd: 目录下存放你的cli各种命令(根命令、子命令等)
- main.go: 项目入口函数
- Init.go: 初始化函数
? appName/
? cmd/
add.go
your.go
commands.go
here.go
root.go
main.go
init.go
root.go 编写go项目可执行程序的根命令(以hugo 为例)
var rootCmd = &cobra.Command{
Use: "hugo",
Short: "Hugo is a very fast static site generator",
Long: `A Fast and Flexible Static Site Generator built with
love by spf13 and friends in Go.
Complete documentation is available at http://hugo.spf13.com`,
Run: func(cmd *cobra.Command, args []string) {
},
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
main.go 一般只需要调用根目录的执行函数即可。
package main
import (
"{pathToYourApp}/cmd"
)
func main() {
cmd.Execute()
}
1.2.2 添加子命令
添加一个子命令add 在add.go 中:
var add = &cobra.Command{
Use: "add [Person]",
Short: "add one item",
Long: `add one item`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
},
}
一般在在项目的init.go 中的init 函数中初始化操作:在root命令下添加这个子命令(可以多行添加多个)
func init() {
cmd.rootCmd.AddCommand(add)
}
1.2.3 添加flag
flag有两种类型:
- Persistent Flags: 持久型的Flag,即设置在根命令上,其本身以及所有子命令都会有这个全局Flag
rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
- Local Flags: 这种flag只应用在特定的命令上
localCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")
Flag 默认是可选的,但是有的命令我们希望其变为必选,可以这样操作:
rootCmd.Flags().StringVarP(&Region, "region", "r", "", "AWS region (required)")
rootCmd.MarkFlagRequired("region")
rootCmd.PersistentFlags().StringVarP(&Region, "region", "r", "", "AWS region (required)")
rootCmd.MarkPersistentFlagRequired("region")
二、Viper
Viper是一个完整的Go应用配置解决方案,包括12-Factor应用。它被设计用于在应用程序中工作,可以处理所有类型的配置需求和格式。
2.1 安装
go get github.com/spf13/viper
2.2 使用
2.2.1 设置默认值
一个好的项目需要在引入配置文件之前使用一些默认值,可以通过以下方式构建
viper.SetDefault("ContentDir", "content")
viper.SetDefault("LayoutDir", "layouts")
viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"})
2.2.2 读取配置文件
支持的配置文件格式:JSON, TOML, YAML, HCL, envfile and Java properties config files
Viper可以搜索多个路径,但目前单个Viper实例只支持单个配置文件。
具体的使用规则如下:
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath("/etc/appname/")
viper.AddConfigPath("$HOME/.appname")
viper.AddConfigPath(".")
err := viper.ReadInConfig()
if err != nil {
panic(fmt.Errorf("Fatal error config file: %w \n", err))
}
2.2.3 写配置文件
有时您希望存储在运行时所做的所有修改。为此,可以使用一些命令,每个命令都有自己的用途:
- WriteConfig——如果存在,则将当前viper配置写入预定义路径。如果没有预定义的路径,则返回错误。将覆盖当前配置文件,如果它存在。
- SafeWriteConfig——将当前的viper配置写入预定义的路径。如果没有预定义的路径,则返回错误。将不会覆盖当前配置文件,如果它存在。
- WriteConfigAs—将当前viper配置写入给定的文件路径。将覆盖给定的文件,如果它存在。
- SafeWriteConfigAs—将当前的viper配置写入给定的文件路径。如果给定的文件存在,则不会覆盖该文件。
所有标记为Safe的函数都不会覆盖任何文件,如果不存在,就创建,而默认行为是创建或截断。
viper.WriteConfig()
viper.SafeWriteConfig()
viper.WriteConfigAs("/path/to/my/.config")
viper.SafeWriteConfigAs("/path/to/my/.config")
viper.SafeWriteConfigAs("/path/to/my/.other_config")
2.2.4 运行时读取配置文件
Viper支持让应用程序在运行时实时读取配置文件的功能。 需要重新启动服务器以使配置生效的日子已经一去不复返了,使用viper的应用程序可以在运行时读取配置文件的更新,不会错过任何信息。
只需告诉viper实例watchConfig。也可以在每次发生更改时提供一个函数让Viper运行。
确保在调用WatchConfig()之前添加了所有的configPaths
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Config file changed:", e.Name)
})
viper.WatchConfig()
2.2.5 从IO流中读取配置文件
viper.SetConfigType("yaml")
var yamlExample = []byte(`
Hacker: true
name: steve
hobbies:
- skateboarding
- snowboarding
- go
clothing:
jacket: leather
trousers: denim
age: 35
eyes : brown
beard: true
`)
viper.ReadConfig(bytes.NewBuffer(yamlExample))
viper.Get("name")
2.2.6 重设值与覆盖、键使用别名
viper.Set("Verbose", true)
viper.Set("LogFile", LogFile)
viper.RegisterAlias("loud", "Verbose")
viper.Set("verbose", true)
viper.Set("loud", true)
viper.GetBool("loud")
viper.GetBool("verbose")
2.2.7 Get值
Viper提供的所有Get值的方法:
Get(key string) : interface{} GetBool(key string) : bool GetFloat64(key string) : float64 GetInt(key string) : int GetIntSlice(key string) : []int GetString(key string) : string GetStringMap(key string) : map[string]interface{} GetStringMapString(key string) : map[string]string GetStringSlice(key string) : []string GetTime(key string) : time.Time GetDuration(key string) : time.Duration IsSet(key string) : bool AllSettings() : map[string]interface{}
需要注意的一件重要的事情是,如果没有找到,每个Get函数将返回一个零值。为了检查给定的键是否存在,提供了IsSet()方法。
获取多重嵌套的Key、数组:
{
"host": {
"address": "localhost",
"ports": [
5799,
6029
]
},
"datastore": {
"metric": {
"host": "127.0.0.1",
"port": 3099
},
"warehouse": {
"host": "198.0.0.1",
"port": 2112
}
}
}
GetInt("host.ports.1")
注意: 如果有与上述方法中. 取健相同,那么就使用该健
{
"datastore.metric.host": "0.0.0.0",
"host": {
"address": "localhost",
"port": 5799
},
"datastore": {
"metric": {
"host": "127.0.0.1",
"port": 3099
},
"warehouse": {
"host": "198.0.0.1",
"port": 2112
}
}
}
GetString("datastore.metric.host")
viper还支持远程在key/value数据库中读取配置,还可以读取环境变量等等高级功能,如果有需求的话可以看官方文档。
三、结合使用
结合使用其实也就是通过viper读取配置文件,然后给cobra的flag使用
serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
觉得不错的话,请点赞关注呦~~你的关注就是博主的动力 关注公众号,查看更多go开发、密码学和区块链科研内容:
|