英文:
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)
// 在这里定义你的标志和配置设置。
// Cobra支持Persistent Flags,如果在这里定义,
// 将成为应用程序的全局标志。
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还支持本地标志,只有在直接调用此操作时才会运行。
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的工作方式有误解。有什么想法吗?
英文:
The documentation in Cobra and Viper are confusing me. I did cobra init fooproject
and then inside the project dir I did cobra add bar
. I have a PersistentFlag
that is named foo
and here is the init function from the root
command.
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")
}
My configuration file looks like this...
---
foo: aFooString
And when I call go run main.go
I see this...
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:
When I call go run main.go bar
I see this...
Using config file: my/gopath/github.com/user/fooproject/.fooproject.yaml
bar called
fooString is:
So it is using the configuration file, but neither one of them seems to be reading it. Maybe I am misunderstanding the way that Cobra and Viper work. Any ideas?
答案1
得分: 11
要将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"))
英文:
To combine spf13/cobra
and spf13/viper
, first define the flag with Cobra:
RootCmd.PersistentFlags().StringP("foo", "", "loaded from config")
Bind it with Viper:
viper.BindPFlag("foo", RootCmd.PersistentFlags().Lookup("foo"))
And get the variable via the Viper's method:
fmt.Println("fooString is: ", viper.GetString("foo"))
答案2
得分: 5
由于Viper值在某些方面不如pflags(例如不支持自定义数据类型),我对“使用Viper检索值”的答案不满意,最终编写了一个小助手类型来将值放回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
}
}
以上是代码的翻译部分。
英文:
Since Viper values are somewhat inferior to pflags (e.g. no support for custom data types), I was not satisfied with answer "use Viper to retrieve values", and ended up writing small helper type to put values back in 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
}
}
答案3
得分: 4
对于遇到相同问题的人来说,问题出在这个调用上:
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())
},
}
英文:
For those facing the same issue, the problem is on this call:
cobra.OnInitialize(initConfig)
The function initConfig is not executed directly but append to an array of initializers https://github.com/spf13/cobra/blob/master/cobra.go#L80
So, the values stored in your config file will be loaded when the Run
field is executed:
rootCmd = &cobra.Command{
Use: "example",
Short: "example cmd",
Run: func(cmd *cobra.Command, args []string) { // OnInitialize is called first
fmt.Println(viper.AllKeys())
},
}
答案4
得分: 1
@WGH的回答是正确的,你可以在rootCmd.PersistenPreRun中使用persistent标志来执行一些操作,这个标志将在所有其他子命令中都可用。
var rootCmd = &cobra.Command{
PersistentPreRun: func(_ *cobra.Command, _ []string) {
if persistentflag {
// 使用persistentflag做一些操作
}
},
}
英文:
@WGH 's answer is correct, you can do something with the persistent flag in rootCmd.PersistenPreRun, which will be available in all other sub commands.
var rootCmd = &cobra.Command{
PersistentPreRun: func(_ *cobra.Command, _ []string) {
if persistentflag {
// do something with persistentflag
}
},
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论