Go 中的命令行标志可以设置为强制吗?

Posted

技术标签:

【中文标题】Go 中的命令行标志可以设置为强制吗?【英文标题】:Can command line flags in Go be set to mandatory? 【发布时间】:2015-10-25 11:55:04 【问题描述】:

有没有办法设置某些标志是强制性的,还是我必须自己检查它们的存在?

【问题讨论】:

【参考方案1】:

flag 包不支持强制或必需标志(意味着必须明确指定标志)。

您可以做的是为(所有)标志使用合理的默认值。如果一个标志类似于没有合理的默认值,请在应用程序开始时检查该值并停止并显示错误消息。无论如何,您都应该进行标志值验证(不仅仅是对必需的标志),所以这不应该意味着任何(大)开销,这通常是一种很好的做法。

【讨论】:

【参考方案2】:

作为already mentioned,flag 包不直接提供此功能,通常您可以(并且应该)能够提供合理的默认值。对于只需要少量显式参数(例如输入和输出文件名)的情况,您可以使用位置参数(例如,在flag.Parse() 之后检查flag.NArg()==2,然后检查input, output := flag.Arg(0), flag.Arg(1))。

但是,如果您遇到不明智的情况;说一些你想以任何顺序接受的整数标志,其中任何整数值都是合理的,但没有默认值。然后您可以使用flag.Visit 函数来检查您关心的标志是否已明确设置。我认为这是判断标志是否显式设置为其默认值的唯一方法(不包括具有保持状态的Set 实现的自定义flag.Value 类型)。

例如,可能是这样的:

    required := []string"b", "s"
    flag.Parse()

    seen := make(map[string]bool)
    flag.Visit(func(f *flag.Flag)  seen[f.Name] = true )
    for _, req := range required 
        if !seen[req] 
            // or possibly use `log.Fatalf` instead of:
            fmt.Fprintf(os.Stderr, "missing required -%s argument/flag\n", req)
            os.Exit(2) // the same exit code flag.Parse uses
        
    

Playground

如果未明确设置“-b”或“-s”标志,则会产生错误。

【讨论】:

【参考方案3】:

go-flags 允许您声明所需的标志和所需的位置参数:

var opts struct 
    Flag string `short:"f" required:"true" name:"a flag"`
    Args struct 
        First   string `positional-arg-name:"first arg"`
        Sencond string `positional-arg-name:"second arg"`
     `positional-args:"true" required:"2"`

args, err := flags.Parse(&opts)

【讨论】:

【参考方案4】:

我喜欢在 CLI 中使用 github.com/jessevdk/go-flags 包。它提供了required 属性,用于设置强制标志:

var opts struct 
...
    // Example of a required flag
    Name string `short:"n" long:"name" description:"A name" required:"true"`
...

【讨论】:

【参考方案5】:

如果你有标志路径,只需检查 *path 是否包含一些值

var path = flag.String("f", "", "/path/to/access.log")
flag.Parse()
if *path == "" 
    usage()
    os.Exit(1)

【讨论】:

可以用usage()代替flag.Usage()【参考方案6】:

我同意this solution,但在我的情况下,默认值通常是环境值。例如,

dsn := flag.String("dsn", os.Getenv("mysql_DSN"), "data source name")

在这种情况下,我想检查这些值是从调用(通常是本地开发)还是环境变量(产品环境)中设置的。

因此,经过一些小的修改,它适用于我的情况。

使用flag.VisitAll检查所有标志的值。

required := []string"b", "s"
flag.Parse()

seen := make(map[string]bool)
flag.VisitAll(func(f *flag.Flag) 
    if f.Value.String() != "" 
        seen[f.Name] = true
    
)
for _, req := range required 
    if !seen[req] 
        // or possibly use `log.Fatalf` instead of:
        fmt.Fprintf(os.Stderr, "missing required -%s argument/flag\n", req)
        os.Exit(2) // the same exit code flag.Parse uses
    

Test example in plauground

【讨论】:

seen 在您的代码中不再意味着“已看到”,而是“空字符串”,此时您最好只检查 flag.Lookup(req).Value.String() != "" 并完全放弃地图。通过以不同的方式从环境中获取值而不是更改标志默认值(例如,可能像 envflag 之类的东西),您也可能会更好。 我同意这个名称,但我想保持与以前的解决方案相同,因为它只是适应不是我的解决方案的一个示例。在使用另一种方式时,我仍然更喜欢使用flag,因为它允许我在开发环境中通过命令行传递参数,并将环境变量留给生产环境。 为了完整起见(我不希望有人使用它),上面链接的envflag 包移动到envflag,因为bitbucket 删除了所有非git 存储库:(。【参考方案7】:

或者您可以使用 docopt,您只需编写“用法”文本。 Docopt 解释使用文本并创建参数映射。这开辟了很多可能性,所有这些都遵循 POSIX 使用文本标准。该库已经支持大约 20 种语言。

https://github.com/docopt/docopt.go

package main

import (
        "fmt"
        "github.com/docopt/docopt-go"
)

const (
        Usage = `Naval Fate.

Usage:
  naval_fate ship new <name>...
  naval_fate ship <name> move <x> <y> [--speed=<kn>]
  naval_fate ship shoot <x> <y>
  naval_fate mine (set|remove) <x> <y> [--moored|--drifting]
  naval_fate -h | --help
  naval_fate --version

Options:
  -h --help     Show this screen.
  --version     Show version.
  --speed=<kn>  Speed in knots [default: 10].
  --moored      Moored (anchored) mine.
  --drifting    Drifting mine.`
)

func main() 
        args, _ := docopt.ParseDoc(Usage)
        fmt.Println(args)

【讨论】:

以上是关于Go 中的命令行标志可以设置为强制吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有“标志”包的情况下在 Go 中获取命令行参数?

27.Go 解析命令行参数

如何在erl中为单个标志设置多个命令行参数

使用 cmake 从命令行使用 /MT 标志编译

如何为命令行强制使用PHP版本?

Go+ 命令行标识教程(4.22)