Golang中Cobra/Viper绑定Flags到RootCmd时的异常行为解析
Golang中Cobra/Viper绑定Flags到RootCmd时的异常行为解析
以下内容(main.go 中的所有内容)可以正常工作
var rootCmd = &cobra.Command{
Use: "myapp",
Short: "Myapp",
PreRunE: func(cmd *cobra.Command, args []string) error {
fmt.Println("binding variables")
if err := viper.BindPFlags(cmd.Flags()); err != nil {
return err
}
fmt.Println(viper.GetString("allow-origin"))
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
return viper.BindPFlags(cmd.Flags())
},
}
func main() {
rootCmd.PersistentFlags().StringP("allow-origin", "", "", "help")
rootCmd.Execute()
▶ go run web/main.go myapp --allow-origin https://111.222.33.4
binding variables
https://111.222.33.4
但是,当 rootCmd 的定义和标志的添加被移到另一个包(cmd)时,以下代码无法正常工作
package main
func main() {
cmd.RootCmd.PersistentFlags().StringP("allow-origin", "", "", "help")
cmd.RootCmd.Execute()
package cmd
var RootCmd = &cobra.Command{
Use: "myapp",
Short: "Myapp",
PreRunE: func(cmd *cobra.Command, args []string) error {
fmt.Println("binding variables")
if err := viper.BindPFlags(cmd.Flags()); err != nil {
return err
}
fmt.Println(viper.GetString("allow-origin"))
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
return viper.BindPFlags(cmd.Flags())
},
}
▶ go run web/main.go myapp --allow-origin https://111.222.33.4
binding variables
也就是说,在第二种情况下,标志 allow-origin 没有被绑定(!)
我遗漏了什么?
更多关于Golang中Cobra/Viper绑定Flags到RootCmd时的异常行为解析的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于Golang中Cobra/Viper绑定Flags到RootCmd时的异常行为解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
问题在于标志的添加时机。当 rootCmd 被定义在 cmd 包中,而标志在 main 包中添加时,PreRunE 执行时标志尚未附加到命令上。
在 Cobra 中,标志需要在命令执行前添加。当你在 main 包中添加标志时,PreRunE 在标志添加之前就被调用了,导致 cmd.Flags() 不包含 allow-origin 标志。
以下是修正后的代码:
main.go
package main
import (
"yourproject/cmd"
)
func main() {
cmd.Execute()
}
cmd/root.go
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var RootCmd = &cobra.Command{
Use: "myapp",
Short: "Myapp",
PreRunE: func(cmd *cobra.Command, args []string) error {
fmt.Println("binding variables")
if err := viper.BindPFlags(cmd.Flags()); err != nil {
return err
}
fmt.Println(viper.GetString("allow-origin"))
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
return viper.BindPFlags(cmd.Flags())
},
}
func init() {
RootCmd.PersistentFlags().StringP("allow-origin", "", "", "help")
}
func Execute() error {
return RootCmd.Execute()
}
关键改动:
- 将标志添加移到
cmd包的init()函数中,确保在命令执行前标志已附加 - 创建
Execute()函数来封装命令执行
这样就能正确绑定标志了:
▶ go run main.go myapp --allow-origin https://111.222.33.4
binding variables
https://111.222.33.4
如果需要在 main 包中配置标志,可以改为:
main.go
package main
import (
"yourproject/cmd"
)
func main() {
cmd.RootCmd.PersistentFlags().StringP("allow-origin", "", "", "help")
cmd.Execute()
}
cmd/root.go
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var RootCmd = &cobra.Command{
Use: "myapp",
Short: "Myapp",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
fmt.Println("binding variables")
if err := viper.BindPFlags(cmd.Flags()); err != nil {
return err
}
fmt.Println(viper.GetString("allow-origin"))
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
return viper.BindPFlags(cmd.Flags())
},
}
func Execute() error {
return RootCmd.Execute()
}
使用 PersistentPreRunE 而不是 PreRunE,确保在子命令中也能正确绑定标志。

