2 回答

TA贡献1963条经验 获得超6个赞
我认为这很有趣,所以我做了一些挖掘,发现你的确切问题记录在一个问题中。有问题的一行是这样的:
config.BindPFlag("name", cmd.Flags().Lookup("name"))
// ^^^^^^^
您创建了一个持久性标志,但将该标志绑定到该属性。如果将代码更改为 bind to ,则即使使用中的此行,一切也将按预期工作:FlagsPersistentFlagsNewCmdRoot
config.BindPFlag("name", cmd.PersistentFlags().Lookup("name"))

TA贡献1848条经验 获得超6个赞
这最终比乍一看要复杂一些,所以虽然这里的其他答案帮助我解决了问题,但我想添加一些细节。
文档中有一些细微差别,如果您刚刚开始使用Cobra,这些细微差别并不是特别清楚。让我们从 PersistentFlags 方法的文档开始:
PersistentFlags 返回在当前命令中专门设置的持久性 FlagSet。
关键在于...在当前命令中。在我的根方法中,我们可以使用,因为root命令是当前命令。我们甚至可以在方法中使用,只要我们不处理子命令。NewCmdRootcmd.PersistentFlags()cmd.PersistentFlags()PersistentPreRun
如果我们要从示例中重写,以便它包含一个子命令,就像这样......cmd/root.go
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var cfgFile string
func NewCmdSubcommand() *cobra.Command {
var cmd = &cobra.Command{
Use: "subcommand",
Short: "An example subcommand",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("This is an example subcommand\n")
},
}
return cmd
}
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("Hello, world\n")
},
}
cmd.PersistentFlags().StringVar(
&cfgFile, "config", "", "config file (default is $HOME/.example.yaml)")
cmd.PersistentFlags().String("name", "", "a name")
cmd.AddCommand(NewCmdSubcommand())
err := config.BindPFlag("name", cmd.PersistentFlags().Lookup("name"))
if err != nil {
panic(err)
}
return cmd
}
func initConfig(cmd *cobra.Command, config *viper.Viper) {
name, err := cmd.PersistentFlags().GetString("name")
if err != nil {
panic(err)
}
fmt.Printf("name = %s\n", name)
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"))
}
...我们会发现它在执行root命令时有效:
$ ./example
name =
name is
Hello, world
但是当我们运行子命令时,它失败了:
[lars@madhatter go]$ ./example subcommand
panic: flag accessed but not defined: name
goroutine 1 [running]:
example/cmd.initConfig(0xc000172000, 0xc0001227e0)
/home/lars/tmp/go/cmd/root.go:55 +0x368
example/cmd.NewCmdRoot.func1(0xc000172000, 0x96eca0, 0x0, 0x0)
/home/lars/tmp/go/cmd/root.go:32 +0x34
github.com/spf13/cobra.(*Command).execute(0xc000172000, 0x96eca0, 0x0, 0x0, 0xc000172000, 0x96eca0)
/home/lars/go/pkg/mod/github.com/spf13/cobra@v1.1.3/command.go:836 +0x231
github.com/spf13/cobra.(*Command).ExecuteC(0xc00011db80, 0x0, 0xffffffff, 0xc0000240b8)
/home/lars/go/pkg/mod/github.com/spf13/cobra@v1.1.3/command.go:960 +0x375
github.com/spf13/cobra.(*Command).Execute(...)
/home/lars/go/pkg/mod/github.com/spf13/cobra@v1.1.3/command.go:897
main.main()
/home/lars/tmp/go/main.go:11 +0x2a
这是因为子命令从根继承命令(这是部分的意思),但是当此方法运行时,传递给的参数不再是根命令;这是命令。当我们尝试调用 时,它会失败,因为当前命令没有任何与之关联的持久标志。PersistentPreRunPersistentcmdPersistentPreRunsubcommandcmd.PersistentFlags()
在这种情况下,我们需要改用 Flags 方法:
Flags 返回适用于此命令的完整 FlagSet(此处和所有父级声明的本地和持久性)。
这使我们能够访问父级声明的持久标志。
另一个问题(似乎没有在文档中明确说明)是,只有在命令处理运行后(即,在调用命令或父级之后)才可用。这意味着我们可以在 中使用它,但我们不能在 中使用它(因为该方法在我们处理命令行之前完成)。Flags()cmd.Execute()PersistentPreRunNewCmdRoot
TL;DR
我们必须使用 in,因为我们正在寻找应用于当前命令的持久标志,并且 from 的值尚不可用。cmd.PersistentFlags()NewCmdRootFlags()
我们需要使用 in(和其他持久命令方法),因为在处理子命令时,只会在当前命令上查找持久标志,但不会遍历父级。我们需要使用,这将汇总父级声明的持久标志。cmd.Flags()PersistentPreRunPersistentFlagscmd.Flags()
- 2 回答
- 0 关注
- 83 浏览
添加回答
举报