全局标志和子命令

我正在实现一个带有多个子命令的小CLI。我想支持全局标志,即适用于所有子命令以避免重复它们的标志。


例如,在下面的示例中,我试图拥有所有子命令所需的标志。-required


package main


import (

    "flag"

    "fmt"

    "log"

    "os"

)


var (

    required = flag.String(

        "required",

        "",

        "required for all commands",

    )

    fooCmd = flag.NewFlagSet("foo", flag.ExitOnError)

    barCmd = flag.NewFlagSet("bar", flag.ExitOnError)

)


func main() {

    flag.Parse()


    if *required == "" {

        fmt.Println("-required is required for all commands")

    }


    switch os.Args[1] {

    case "foo":

        fooCmd.Parse(os.Args[2:])

        fmt.Println("foo")

    case "bar":

        barCmd.Parse(os.Args[2:])

        fmt.Println("bar")

    default:

        log.Fatalf("[ERROR] unknown subcommand '%s', see help for more details.", os.Args[1])

    }

}

我希望用法是这样的:


$ go run main.go foo -required helloworld

但是如果我用上面的代码运行它,我得到:


$ go run main.go foo -required hello

-required is required for all commands

flag provided but not defined: -required

Usage of foo:

exit status 2

它看起来像是没有从CLI捕获,然后抱怨我给了它一个它无法识别的标志。flag.Parse()-requiredfooCmd


在 Golang 中使用带有全局标志的子命令的最简单方法是什么?


炎炎设计
浏览 82回答 2
2回答

杨__羊羊

如果打算实现子命令,则不应调用 flag。解析())。相反,请决定使用哪个子命令(就像您所做的那样),并仅调用其 FlagSet.Parse() 方法。os.Args[1]是的,要使此操作有效,所有标志集都应包含公共标志。但是,只需注册一次(在一个地方)就很容易了。创建包级别变量:var (    required string    fooCmd = flag.NewFlagSet("foo", flag.ExitOnError)    barCmd = flag.NewFlagSet("bar", flag.ExitOnError))使用循环遍历所有标志集,并注册公共标志,使用FlagSet.StringVar()指向您的变量:func setupCommonFlags() {    for _, fs := range []*flag.FlagSet{fooCmd, barCmd} {        fs.StringVar(            &required,            "required",            "",            "required for all commands",        )    }}并在调用适当的标志集,并在之后进行测试:main()Parse()requiredfunc main() {    setupCommonFlags()    switch os.Args[1] {    case "foo":        fooCmd.Parse(os.Args[2:])        fmt.Println("foo")    case "bar":        barCmd.Parse(os.Args[2:])        fmt.Println("bar")    default:        log.Fatalf("[ERROR] unknown subcommand '%s', see help for more details.", os.Args[1])    }    if required == "" {        fmt.Println("-required is required for all commands")    }}您可以通过创建标志集映射来改进上述解决方案,因此可以使用该映射注册公共标志,还可以执行解析。完整应用:var (    required string    fooCmd = flag.NewFlagSet("foo", flag.ExitOnError)    barCmd = flag.NewFlagSet("bar", flag.ExitOnError))var subcommands = map[string]*flag.FlagSet{    fooCmd.Name(): fooCmd,    barCmd.Name(): barCmd,}func setupCommonFlags() {    for _, fs := range subcommands {        fs.StringVar(            &required,            "required",            "",            "required for all commands",        )    }}func main() {    setupCommonFlags()    cmd := subcommands[os.Args[1]]    if cmd == nil {        log.Fatalf("[ERROR] unknown subcommand '%s', see help for more details.", os.Args[1])    }    cmd.Parse(os.Args[2:])    fmt.Println(cmd.Name())    if required == "" {        fmt.Println("-required is required for all commands")    }}

翻翻过去那场雪

将全局标志放在子命令之前:go run . -required=x foo.使用代替 :flag.Args()os.Argspackage mainimport (    "flag"    "fmt"    "log"    "os")var (    required = flag.String(        "required",        "",        "required for all commands",    )       fooCmd = flag.NewFlagSet("foo", flag.ExitOnError)    barCmd = flag.NewFlagSet("bar", flag.ExitOnError))func main() {    flag.Parse()    if *required == "" {        fmt.Println("-required is required for all commands")    }       args := flag.Args() // everything after the -required flag, e.g. [foo, -foo-flag-1, -foo-flag-2, ...]    switch args[0] {    case "foo":        fooCmd.Parse(args[1:])        fmt.Println("foo")    case "bar":        barCmd.Parse(args[1:])        fmt.Println("bar")    default:        log.Fatalf("[ERROR] unknown subcommand '%s', see help for more details.", args[0])    }   }如果要将所有标志放在一起,请在子命令之后编写一个帮助程序函数,该函数将公共标志添加到每个标志集:var (    fooCmd = flag.NewFlagSet("foo", flag.ExitOnError)    barCmd = flag.NewFlagSet("bar", flag.ExitOnError))type globalOpts struct {    required string}func main() {    var opts globalOpts    addGlobalFlags(fooCmd, &opts)    addGlobalFlags(barCmd, &opts)    if opts.required == "" {        fmt.Println("-required is required for all commands")    }     // ...}func addGlobalFlags(fs *flag.FlagSet, opts *globalOpts) {    fs.StringVar(        &opts.required,        "required",        "",        "required for all commands",    )}也许您还可以将这两种方法结合起来,使全局标志在任何位置工作。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go