【问题标题】:How is a Cobra Flag of type `StringToStringVar` access using Viper?如何使用 Viper 访问“StringToStringVar”类型的 Cobra Flag?
【发布时间】:2019-10-20 15:39:51
【问题描述】:

我正在尝试在 Go 中开发一个应用程序,它接受命令行上的输入作为键值对字符串。为此,我使用 Cobra 库中的 StrngToStringVar。

我还使用 Viper 将这些标志绑定到配置,但是无论我输入什么值,我似乎都无法从 Viper 获取它。

这是代码

package main

import (
    "fmt"

    "github.com/spf13/cobra"
    "github.com/spf13/viper"

    "github.com/davecgh/go-spew/spew"
)

func main() {
    var items map[string]string
    var name string

    rootCmd := &cobra.Command{
        Use:   "cobra",
        Short: "Test options on the command line",
        Long:  ``,
        Run: func(ccmd *cobra.Command, args []string) {

            spew.Dump(viper.GetString("name"))
            spew.Println("")

            fmt.Println("GetStringMap")
            spew.Dump(viper.GetStringMap("items"))

            fmt.Println("")

            fmt.Println("GetStringMapString")
            spew.Dump(viper.GetStringMapString("items"))
        },
    }

    rootCmd.Flags().StringVar(&name, "name", "", "Name of the list")
    rootCmd.Flags().StringToStringVar(&items, "item", nil, "Map stating the items to be included")

    viper.BindPFlag("name", rootCmd.Flags().Lookup("name"))
    viper.BindPFlag("items", rootCmd.Flags().Lookup("item"))

    rootCmd.Execute()
}

如果我使用命令 go run .\main.go --item shopping=apple,banana --name foobar 运行它,我会得到以下结果

(string) (len=6) "foobar"

GetStringMap
(map[string]interface {}) {
}

GetStringMapString
(map[string]string) {
}

可以看出,即使我正确设置了输入(我相信),他的输出也不包含任何项目。我一直在尝试使用 PR https://github.com/spf13/pflag/pull/133 来弄清楚如何做到这一点,但我没有任何运气。

我想知道绑定是否不正确,但是我在其他项目中成功使用过CObra,所以我对如何引用来自Cobra 生成的map[string]string 缺乏了解。

【问题讨论】:

  • 您是否尝试使用变量items 和names 作为包变量

标签: go go-cobra viper-go


【解决方案1】:

就绑定部分而言,是正确的。但是,在处理之前,您可以使用 vipver.IsSet() 验证是否存在特定密钥,使用 viper.AllKeys() 打印所有可用密钥或使用 viper.AllSettings() 打印所有内容。

这是一个示例 (test.go):

package main

import (
    "fmt"

    "github.com/spf13/cobra"
    "github.com/spf13/viper"
)

func main() {
    var items map[string]string

    rootCmd := &cobra.Command{
        Use: "app",
        Run: func(ccmd *cobra.Command, args []string) {

            fmt.Println("item exists?", viper.IsSet("item"))
            fmt.Println("GetString  :", viper.GetString("item"))
            fmt.Println("Keys       :", viper.AllKeys())
            fmt.Println("Settings   :", viper.AllSettings())
        },
    }

    rootCmd.Flags().StringToStringVarP(&items, "item", "i", nil, "Map stating the items to be included")
    viper.BindPFlag("item", rootCmd.Flags().Lookup("item"))
    rootCmd.Execute()
}

输出:

$ ./test.exe -i 'k=v,"a=b,c"'
item exists? true
GetString  : [k=v,"a=b,c"]
Keys       : [item]
Settings   : map[item:[k=v,"a=b,c"]]

GetStringMap() 和 GetStringMapString() 的使用对 JSON、YAML 等其他格式更有意义。您可以将这些函数的执行跟踪到调试模式下的 JSON 解组调用。

对于地图转换,您可以像这样编写自己的函数(live):

func getStringMap(s string) map[string]string {
    entries := strings.Split(s, ",")

    m := make(map[string]string)
    for _, e := range entries {
        tokens := strings.Split(e, "=")
        k := strings.TrimSpace(tokens[0])
        v := strings.TrimSpace(tokens[1])
        m[k] = v
    }

    return m
}

【讨论】:

  • 感谢您,viper 设置的打印结果确实帮助我了解了发生了什么。我遇到了./text.exe -i 'k=v,"a=b,c" 导致错误Error: invalid argument "k=v,b=c,d" for "-i, --item" flag: d must be formatted as key=value 的问题,但我意识到我可以使用多个-i 添加更多项目。
  • @RussellSeymour:不客气! :) 是的,你可以多次使用一个标志来达到同样的效果。但是,它应该适用于多个值以及单个标志,例如./test.exe -i 'k=v,"a=b,c"'。在您的评论中,缺少尾随引号 '。是不是打错字了?
  • 是的,这是一个错字,当我测试它时,我确实在命令行上有尾随 '。我不介意我可以多次使用-i,事实上我认为它更容易记录:-)。感谢您的帮助。
  • @RussellSeymour:对。我的荣幸。很高兴它有帮助。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-05-25
  • 1970-01-01
  • 1970-01-01
  • 2021-01-01
  • 1970-01-01
  • 2021-04-09
  • 1970-01-01
相关资源
最近更新 更多