在 Cobra 命令行工具中,如何为不同的标志使用相同的变量?

In Cobra command-line tool, how to use the same variable for different flags?

此示例 Cobra 应用程序 https://github.com/kurtpeek/myCobraApp 包含一个使用 Cobra 生成器和以下命令构建的 Cobra 应用程序脚手架:

cobra add serve
cobra add config

目录结构为

.
├── LICENSE
├── cmd
│   ├── config.go
│   ├── root.go
│   └── serve.go
├── go.mod
├── go.sum
└── main.go

config.go 中,字符串变量 deviceUUID 被定义并绑定到该命令的标志,默认值为 "configDeviceUUID":

var deviceUUID string

func init() {
    rootCmd.AddCommand(configCmd)

    // Cobra supports local flags which will only run when this command
    // is called directly, e.g.:
    configCmd.Flags().StringVar(&deviceUUID, "deviceUUID", "configDeviceUUID", "Device UUID")
    fmt.Println("deviceUUID after config init:", deviceUUID)
}

类似地,在 serve.go 中,deviceUUID 变量绑定到本地标志:

func init() {
    rootCmd.AddCommand(serveCmd)

    serveCmd.Flags().StringVar(&deviceUUID, "deviceUUID", "serveDeviceUUID", "Device UUID")
    fmt.Println("deviceUUID after serve init:", deviceUUID)
}

问题是,如果我 运行 config 命令没有在命令行指定 deviceUUID 标志,它会从 serve命令:

> go run main.go config
deviceUUID after config init: configDeviceUUID
deviceUUID after serve init: serveDeviceUUID
deviceUUID: serveDeviceUUID
config called

似乎正在发生的事情是每个文件中的 init() 函数按字母顺序排列 运行,最后一个 运行 设置标志的默认值。

如何避免这种行为?我希望 config.go 中设置的默认值始终应用于 config 命令。 (当然,我可以声明单独的变量,如 configDeviceUUIDserveDeviceUUID,但这对我来说似乎有点混乱)。

我不知道为什么你会认为凌乱。你有一个变量,你在几个 init 函数中设置了它,所以 last 设置是保持设置的那个:你对正在发生的事情的解释是正确的。显然,这实际上是两个不同的变量,只是其中最多一个会被 used.

(假设还有其他命令做其他事情,也许 none 这些全局变量将被使用。如果 Go 是一种自然更动态的语言其中 一切 都是在运行时决定的,或者如果您使用的是更动态的运行时设置,您可以创建其中的 none 。但这不是 Go:Go 是充满静态类型和静态变量,创建于 link 时间。)

如果你真的只想使用一个全局变量,那么显而易见的解决方案是选择一些标记值来表示未设置,并将其设为默认值眼镜蛇套装。然后,如果变量在 运行 命令中保持 "not set" 值,则您知道用户没有提供值。

如果空字符串适合作为标记——通常是这种情况——这很容易。您的每个命令都会增加几行:

if deviceUUID == "" {
    deviceUUID = defaultDeviceUUID
}

大功告成。

如果 cobra 代码将默认初始化程序值保存在某处 private,这可能会更好,或者至少对您来说更方便:

configCmd.Flags().StringVar(&deviceUUID, "deviceUUID", "configDeviceUUID", "Device UUID")

然后将那些私有的、保存的默认值复制到(单个)全局变量中当顶级命令决定使用config子命令时,而不是不将它保存在私有的某个地方,并在调用 configCmd.Flags().StringVar() 函数时将其复制到 中的(单个)全局变量中 ,但事实并非如此确实有效。所以:

configCmd.Flags().StringVar(&deviceUUID, "deviceUUID", "", "Device UUID")

加上上面的空字符串测试可能就足够了。请注意,每个 init 仍然每次都覆盖(单个,共享,甚至可能从未使用过)全局变量,但它们都将其从初始空字符串值设置为新的空字符串 -字符串值,保持不变。