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/[email protected]/command.go:836 +0x231
github.com/spf13/cobra.(*Command).ExecuteC(0xc00011db80, 0x0, 0xffffffff, 0xc0000240b8)
/home/lars/go/pkg/mod/github.com/spf13/[email protected]/command.go:960 +0x375
github.com/spf13/cobra.(*Command).Execute(...)
/home/lars/go/pkg/mod/github.com/spf13/[email protected]/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 關注
- 106 瀏覽
添加回答
舉報