Golang中如何使用CLI参数和配置文件来赋值
Golang中如何使用CLI参数和配置文件来赋值 我有一个工具,希望实现以下功能:
- 查找默认配置文件(已完成)
- 将数据解组到结构体的字段中(已完成)
- 使用 flag 包解析所有命令行参数(已完成)
- 如果参数值不等于结构体字段值,则相应地更新字段
- 或者,也许我应该完全重新考虑这个方法 🙂
以下是一些代码片段:
type Config struct {
User string `json:"user"`
Port int `json:"port"`
Key string `json:"key"`
Nodes string `json:"nodes"`
Syslog bool `json:"syslog"`
LogPath string `json:"logpath"`
Fatal bool `json:"fatal"`
}
// 这是我原以为最佳方法的样板代码
func (c *Config) Rectify(uname string) {
c.User = uname
}
func getConfig(fd string) {
contents, e := ioutil.ReadFile(fd)
check(e)
e = json.Unmarshal(contents, &cfg)
check(e)
}
func main() {
// 解组;赋值给 cfg,此处为示例硬编码
getConfig("/home/rxlx/.send_config.json")
uname := flag.String("u", "send", "username")
// 为简洁起见,省略了其他标志
flag.Parse()
}
这就是我目前有点卡住的地方。我试图简化该工具的命令行使用方式,同时使其更符合 Go 语言的风格(或者说更地道?)。
谢谢
更多关于Golang中如何使用CLI参数和配置文件来赋值的实战教程也可以访问 https://www.itying.com/category-94-b0.html
2 回复
在Go中处理CLI参数和配置文件时,可以使用flag包配合结构体反射来实现优先级覆盖。以下是完整的解决方案:
package main
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"os"
"reflect"
)
type Config struct {
User string `json:"user"`
Port int `json:"port"`
Key string `json:"key"`
Nodes string `json:"nodes"`
Syslog bool `json:"syslog"`
LogPath string `json:"logpath"`
Fatal bool `json:"fatal"`
}
var cfg Config
func getConfig(fd string) error {
contents, err := ioutil.ReadFile(fd)
if err != nil {
return err
}
return json.Unmarshal(contents, &cfg)
}
func updateConfigFromFlags() {
// 定义命令行标志
userFlag := flag.String("user", cfg.User, "username")
portFlag := flag.Int("port", cfg.Port, "port number")
keyFlag := flag.String("key", cfg.Key, "access key")
nodesFlag := flag.String("nodes", cfg.Nodes, "node list")
syslogFlag := flag.Bool("syslog", cfg.Syslog, "enable syslog")
logPathFlag := flag.String("logpath", cfg.LogPath, "log file path")
fatalFlag := flag.Bool("fatal", cfg.Fatal, "fatal errors")
flag.Parse()
// 使用反射检查并更新字段
v := reflect.ValueOf(&cfg).Elem()
t := v.Type()
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
fieldName := t.Field(i).Name
switch fieldName {
case "User":
if *userFlag != cfg.User {
field.SetString(*userFlag)
}
case "Port":
if *portFlag != cfg.Port {
field.SetInt(int64(*portFlag))
}
case "Key":
if *keyFlag != cfg.Key {
field.SetString(*keyFlag)
}
case "Nodes":
if *nodesFlag != cfg.Nodes {
field.SetString(*nodesFlag)
}
case "Syslog":
if *syslogFlag != cfg.Syslog {
field.SetBool(*syslogFlag)
}
case "LogPath":
if *logPathFlag != cfg.LogPath {
field.SetString(*logPathFlag)
}
case "Fatal":
if *fatalFlag != cfg.Fatal {
field.SetBool(*fatalFlag)
}
}
}
}
func main() {
// 1. 加载默认配置
err := getConfig("/home/rxlx/.send_config.json")
if err != nil {
fmt.Printf("Error loading config: %v\n", err)
os.Exit(1)
}
// 2. 解析命令行参数并更新配置
updateConfigFromFlags()
// 3. 使用最终配置
fmt.Printf("Final config: %+v\n", cfg)
}
更简洁的版本使用flag的Var方法:
package main
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"os"
)
type Config struct {
User string `json:"user"`
Port int `json:"port"`
Key string `json:"key"`
Nodes string `json:"nodes"`
Syslog bool `json:"syslog"`
LogPath string `json:"logpath"`
Fatal bool `json:"fatal"`
}
func (c *Config) UpdateFromFlags() {
// 使用当前配置值作为flag的默认值
flag.StringVar(&c.User, "user", c.User, "username")
flag.IntVar(&c.Port, "port", c.Port, "port number")
flag.StringVar(&c.Key, "key", c.Key, "access key")
flag.StringVar(&c.Nodes, "nodes", c.Nodes, "node list")
flag.BoolVar(&c.Syslog, "syslog", c.Syslog, "enable syslog")
flag.StringVar(&c.LogPath, "logpath", c.LogPath, "log file path")
flag.BoolVar(&c.Fatal, "fatal", c.Fatal, "fatal errors")
flag.Parse()
}
func main() {
cfg := &Config{}
// 加载配置文件
contents, err := ioutil.ReadFile("/home/rxlx/.send_config.json")
if err == nil {
json.Unmarshal(contents, cfg)
}
// 解析命令行参数(会覆盖配置文件中的值)
cfg.UpdateFromFlags()
fmt.Printf("Config: %+v\n", cfg)
}
使用第三方库github.com/spf13/viper的解决方案:
package main
import (
"fmt"
"github.com/spf13/viper"
)
type Config struct {
User string
Port int
Key string
Nodes string
Syslog bool
LogPath string
Fatal bool
}
func main() {
viper.SetConfigName(".send_config")
viper.SetConfigType("json")
viper.AddConfigPath("/home/rxlx")
viper.AddConfigPath(".")
// 设置默认值
viper.SetDefault("user", "send")
viper.SetDefault("port", 8080)
// 读取配置文件
viper.ReadInConfig()
// 绑定命令行参数
viper.BindPFlag("user", flag.Lookup("user"))
viper.BindPFlag("port", flag.Lookup("port"))
flag.Parse()
// 自动将配置解析到结构体
var cfg Config
viper.Unmarshal(&cfg)
fmt.Printf("Config: %+v\n", cfg)
}
这种方法确保了命令行参数的优先级高于配置文件,同时保持了代码的简洁性。


