我最近才开始使用 Go,并且在使用 Cobra 和 Viper 时遇到了一些我不确定我是否理解的行为。
这是您通过运行获得的示例代码的略微修改版本cobra init
。在main.go
我有:
package main
import (
"github.com/larsks/example/cmd"
"github.com/spf13/cobra"
)
func main() {
rootCmd := cmd.NewCmdRoot()
cobra.CheckErr(rootCmd.Execute())
}
在cmd/root.go
我有:
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var cfgFile string
func NewCmdRoot() *cobra.Command {
config := viper.New()
var cmd = &cobra.Command{
Use: "example",
Short: "A brief description of your application",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
initConfig(cmd, config)
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("This is a test\n")
},
}
cmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.example.yaml)")
cmd.PersistentFlags().String("name", "", "a name")
// *** If I move this to the top of initConfig
// *** the code runs correctly.
config.BindPFlag("name", cmd.Flags().Lookup("name"))
return cmd
}
func initConfig(cmd *cobra.Command, config *viper.Viper) {
if cfgFile != "" {
// Use config file from the flag.
config.SetConfigFile(cfgFile)
} else {
config.AddConfigPath(".")
config.SetConfigName(".example")
}
config.AutomaticEnv() // read in environment variables that match
// If a config file is found, read it in.
if err := config.ReadInConfig(); err == nil {
fmt.Fprintln(os.Stderr, "Using config file:", config.ConfigFileUsed())
}
// *** This line triggers a nil pointer reference.
fmt.Printf("name is %s\n", config.GetString("name"))
}
此代码将在最终调用时出现 nil 指针引用恐慌
fmt.Printf
:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x50 pc=0x6a90e5]
如果我将调用config.BindPFlag
从NewCmdRoot
函数移到命令的顶部initConfig
,一切运行都没有问题。
这里发生了什么?根据有关使用的 Viper 文档
BindPFlags
:
和 BindEnv 一样,值不是在调用绑定方法时设置,而是在访问时设置。这意味着您可以尽可能早地绑定,即使在 init() 函数中也是如此。
这几乎正是我在这里所做的。在我打电话的时候
config.BindPflag
,config
是非零,cmd
是非零,并且
name
参数已经被注册。
config
我认为我在 in 的闭包中使用了一些事情PersistentPreRun
,但我不知道为什么会导致这个失败。