为了账号安全,请及时绑定邮箱和手机立即绑定

全局标志和子命令

全局标志和子命令

Go
炎炎设计 2022-09-05 15:48:03
我正在实现一个带有多个子命令的小CLI。我想支持全局标志,即适用于所有子命令以避免重复它们的标志。例如,在下面的示例中,我试图拥有所有子命令所需的标志。-requiredpackage 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")    }    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 commandsflag provided but not defined: -requiredUsage of foo:exit status 2它看起来像是没有从CLI捕获,然后抱怨我给了它一个它无法识别的标志。flag.Parse()-requiredfooCmd在 Golang 中使用带有全局标志的子命令的最简单方法是什么?
查看完整描述

2 回答

?
杨__羊羊

TA贡献1943条经验 获得超7个赞

如果打算实现子命令,则不应调用 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()required


func 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")

    }

}


查看完整回答
反对 回复 2022-09-05
?
翻翻过去那场雪

TA贡献2065条经验 获得超13个赞

将全局标志放在子命令之前:


go run . -required=x foo.


使用代替 :flag.Args()os.Args


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

    }   


    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",

    )

}

也许您还可以将这两种方法结合起来,使全局标志在任何位置工作。


查看完整回答
反对 回复 2022-09-05
  • 2 回答
  • 0 关注
  • 81 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信