1

这个 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命令。(当然,我可以声明单独的变量,例如configDeviceUUIDand serveDeviceUUID,但这对我来说似乎有点混乱)。

4

1 回答 1

1

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

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

但是,如果您确实只想使用一个全局变量,显而易见的解决方案是选择一些标记值来表示未设置,并将其设为 Cobra 设置的默认值。然后,如果变量在运行命令时保持“未设置”值,您就知道用户没有提供值。

如果空字符串适合作为这样的哨兵——通常就是这种情况——这很容易。您的每个命令都有几行:

if deviceUUID == "" {
    deviceUUID = defaultDeviceUUID
}

你就完成了。

如果 cobra 代码在您这样做时将默认初始化器值保存在某个私有位置,这可能会很好,或者至少对您来说更方便:

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

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

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

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

于 2020-02-28T01:44:05.880 回答