为什么 Cobra 不读取我的配置文件?
Why is Cobra not reading my configuration file?
Cobra 和 Viper 中的文档让我感到困惑。我做了 cobra init fooproject
,然后在项目目录中我做了 cobra add bar
。我有一个名为 foo
的 PersistentFlag
,这里是来自 root
命令的初始化函数。
func Execute() {
if err := RootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(-1)
}
fmt.Println(cfgFile)
fmt.Println("fooString is: ", fooString)
}
func init() {
cobra.OnInitialize(initConfig)
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags, which, if defined here,
// will be global for your application.
RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.fooproject.yaml)")
RootCmd.PersistentFlags().StringVar(&fooString, "foo", "", "loaded from config")
viper.BindPFlag("foo", RootCmd.PersistentFlags().Lookup("foo"))
// Cobra also supports local flags, which will only run
// when this action is called directly.
RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
我的配置文件是这样的...
---
foo: aFooString
当我打电话给 go run main.go
时,我看到了这个...
A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.
Usage:
fooproject [command]
Available Commands:
bar A brief description of your command
help Help about any command
Flags:
--config string config file (default is $HOME/.fooproject.yaml)
--foo string loaded from config
-h, --help help for fooproject
-t, --toggle Help message for toggle
Use "fooproject [command] --help" for more information about a command.
fooString is:
当我打电话给 go run main.go bar
时,我看到了这个...
Using config file: my/gopath/github.com/user/fooproject/.fooproject.yaml
bar called
fooString is:
所以它正在使用配置文件,但他们似乎都没有在读取它。也许我误解了 Cobra 和 Viper 的工作方式。有什么想法吗?
要结合spf13/cobra
和spf13/viper
,首先用Cobra定义标志:
RootCmd.PersistentFlags().StringP("foo", "", "loaded from config")
与 Viper 绑定:
viper.BindPFlag("foo", RootCmd.PersistentFlags().Lookup("foo"))
并通过 Viper 的方法获取变量:
fmt.Println("fooString is: ", viper.GetString("foo"))
由于 Viper 的值在某种程度上不如 pflags(例如,不支持自定义数据类型),我对答案不满意 "use Viper to retrieve values",最后写了一个小的辅助类型来将值放回 pflags。
type viperPFlagBinding struct {
configName string
flagValue pflag.Value
}
type viperPFlagHelper struct {
bindings []viperPFlagBinding
}
func (vch *viperPFlagHelper) BindPFlag(configName string, flag *pflag.Flag) (err error) {
err = viper.BindPFlag(configName, flag)
if err == nil {
vch.bindings = append(vch.bindings, viperPFlagBinding{configName, flag.Value})
}
return
}
func (vch *viperPFlagHelper) setPFlagsFromViper() {
for _, v := range vch.bindings {
v.flagValue.Set(viper.GetString(v.configName))
}
}
func main() {
var rootCmd = &cobra.Command{}
var viperPFlagHelper viperPFlagHelper
rootCmd.PersistentFlags().StringVar(&config.Password, "password", "", "API server password (remote HTTPS mode only)")
viperPFlagHelper.BindPFlag("password", rootCmd.Flag("password"))
rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
err := viper.ReadInConfig()
if err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
return err
}
}
viperPFlagHelper.setPFlagsFromViper()
return nil
}
}
对于那些面临同样问题的人,问题出在这个电话上:
cobra.OnInitialize(initConfig)
函数 initConfig 没有直接执行,而是附加到一个初始化数组中 https://github.com/spf13/cobra/blob/master/cobra.go#L80
因此,当执行 Run
字段时,将加载配置文件中存储的值:
rootCmd = &cobra.Command{
Use: "example",
Short: "example cmd",
Run: func(cmd *cobra.Command, args []string) { // OnInitialize is called first
fmt.Println(viper.AllKeys())
},
}
@WGH 的回答是正确的,你可以用 rootCmd.PersistenPreRun 中的持久标志做一些事情,这将在所有其他子命令中可用。
var rootCmd = &cobra.Command{
PersistentPreRun: func(_ *cobra.Command, _ []string) {
if persistentflag {
// do something with persistentflag
}
},
Cobra 和 Viper 中的文档让我感到困惑。我做了 cobra init fooproject
,然后在项目目录中我做了 cobra add bar
。我有一个名为 foo
的 PersistentFlag
,这里是来自 root
命令的初始化函数。
func Execute() {
if err := RootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(-1)
}
fmt.Println(cfgFile)
fmt.Println("fooString is: ", fooString)
}
func init() {
cobra.OnInitialize(initConfig)
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags, which, if defined here,
// will be global for your application.
RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.fooproject.yaml)")
RootCmd.PersistentFlags().StringVar(&fooString, "foo", "", "loaded from config")
viper.BindPFlag("foo", RootCmd.PersistentFlags().Lookup("foo"))
// Cobra also supports local flags, which will only run
// when this action is called directly.
RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
我的配置文件是这样的...
---
foo: aFooString
当我打电话给 go run main.go
时,我看到了这个...
A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.
Usage:
fooproject [command]
Available Commands:
bar A brief description of your command
help Help about any command
Flags:
--config string config file (default is $HOME/.fooproject.yaml)
--foo string loaded from config
-h, --help help for fooproject
-t, --toggle Help message for toggle
Use "fooproject [command] --help" for more information about a command.
fooString is:
当我打电话给 go run main.go bar
时,我看到了这个...
Using config file: my/gopath/github.com/user/fooproject/.fooproject.yaml
bar called
fooString is:
所以它正在使用配置文件,但他们似乎都没有在读取它。也许我误解了 Cobra 和 Viper 的工作方式。有什么想法吗?
要结合spf13/cobra
和spf13/viper
,首先用Cobra定义标志:
RootCmd.PersistentFlags().StringP("foo", "", "loaded from config")
与 Viper 绑定:
viper.BindPFlag("foo", RootCmd.PersistentFlags().Lookup("foo"))
并通过 Viper 的方法获取变量:
fmt.Println("fooString is: ", viper.GetString("foo"))
由于 Viper 的值在某种程度上不如 pflags(例如,不支持自定义数据类型),我对答案不满意 "use Viper to retrieve values",最后写了一个小的辅助类型来将值放回 pflags。
type viperPFlagBinding struct {
configName string
flagValue pflag.Value
}
type viperPFlagHelper struct {
bindings []viperPFlagBinding
}
func (vch *viperPFlagHelper) BindPFlag(configName string, flag *pflag.Flag) (err error) {
err = viper.BindPFlag(configName, flag)
if err == nil {
vch.bindings = append(vch.bindings, viperPFlagBinding{configName, flag.Value})
}
return
}
func (vch *viperPFlagHelper) setPFlagsFromViper() {
for _, v := range vch.bindings {
v.flagValue.Set(viper.GetString(v.configName))
}
}
func main() {
var rootCmd = &cobra.Command{}
var viperPFlagHelper viperPFlagHelper
rootCmd.PersistentFlags().StringVar(&config.Password, "password", "", "API server password (remote HTTPS mode only)")
viperPFlagHelper.BindPFlag("password", rootCmd.Flag("password"))
rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
err := viper.ReadInConfig()
if err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
return err
}
}
viperPFlagHelper.setPFlagsFromViper()
return nil
}
}
对于那些面临同样问题的人,问题出在这个电话上:
cobra.OnInitialize(initConfig)
函数 initConfig 没有直接执行,而是附加到一个初始化数组中 https://github.com/spf13/cobra/blob/master/cobra.go#L80
因此,当执行 Run
字段时,将加载配置文件中存储的值:
rootCmd = &cobra.Command{
Use: "example",
Short: "example cmd",
Run: func(cmd *cobra.Command, args []string) { // OnInitialize is called first
fmt.Println(viper.AllKeys())
},
}
@WGH 的回答是正确的,你可以用 rootCmd.PersistenPreRun 中的持久标志做一些事情,这将在所有其他子命令中可用。
var rootCmd = &cobra.Command{
PersistentPreRun: func(_ *cobra.Command, _ []string) {
if persistentflag {
// do something with persistentflag
}
},