亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

為什么我收到一個 nil 指針錯誤,這取決于我調用 BindPFlag 的位置?

為什么我收到一個 nil 指針錯誤,這取決于我調用 BindPFlag 的位置?

Go
滄海一幻覺 2022-09-05 09:46:38
我最近剛開始與Go合作,我遇到了一些與Cobra和Viper合作的行為,我不確定我是否理解。這是您通過運行 獲得的示例代碼的略微修改版本。在我有:cobra initmain.gopackage mainimport (    "github.com/larsks/example/cmd"    "github.com/spf13/cobra")func main() {    rootCmd := cmd.NewCmdRoot()    cobra.CheckErr(rootCmd.Execute())}在我有:cmd/root.gopackage cmdimport (    "fmt"    "os"    "github.com/spf13/cobra"    "github.com/spf13/viper")var cfgFile stringfunc 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")    }}此代碼將在最終調用時出現 nil 指針引用,從而導致恐慌:fmt.Printfpanic: runtime error: invalid memory address or nil pointer dereference[signal SIGSEGV: segmentation violation code=0x1 addr=0x50 pc=0x6a90e5]如果我將調用從函數移動到命令的頂部,則一切都運行沒有問題。config.BindPFlagNewCmdRootinitConfig這是怎么回事?根據蝰蛇關于使用的文檔:BindPFlags與 BindEnv 一樣,該值不是在調用綁定方法時設置的,而是在訪問綁定方法時設置的。這意味著您可以根據需要盡早綁定,即使在 init() 函數中也是如此。這幾乎就是我在這里所做的。在我調用時,是非 nil,是非 nil,并且參數已被注冊。config.BindPflagconfigcmdname我假設我在 中的閉包中使用了一些東西,但我不知道為什么這會導致這種失敗。configPersistentPreRun
查看完整描述

2 回答

?
神不在的星期二

TA貢獻1963條經驗 獲得超6個贊


我認為這很有趣,所以我做了一些挖掘,發現你的確切問題記錄在一個問題中。有問題的一行是這樣的:


config.BindPFlag("name", cmd.Flags().Lookup("name"))

//                           ^^^^^^^

您創建了一個持久性標志,但將該標志綁定到該屬性。如果將代碼更改為 bind to ,則即使使用中的此行,一切也將按預期工作:FlagsPersistentFlagsNewCmdRoot


config.BindPFlag("name", cmd.PersistentFlags().Lookup("name"))


查看完整回答
反對 回復 2022-09-05
?
慕勒3428872

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()


查看完整回答
反對 回復 2022-09-05
  • 2 回答
  • 0 關注
  • 106 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號