Go语言中的命令行标志是否可以设置为必需的?

huangapple go评论75阅读模式
英文:

Can command line flags in Go be set to mandatory?

问题

有没有一种方法可以设置某些标志为必填项,或者我必须自己检查它们是否存在?

英文:

Is there a way how to set that certain flags are mandatory, or do I have to check for their presence on my own?

答案1

得分: 58

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

你可以为(所有)标志使用合理的默认值。如果一个标志没有合理的默认值,你可以在应用程序开始时检查该值,并显示错误消息停止程序运行。你应该对标志值进行验证(不仅仅是对必需的标志),所以这不会带来太大的额外开销,并且这是一个好的通用实践。

英文:

The flag package does not support mandatory or required flags (meaning the flag must be specified explicitly).

What you can do is use sensible default values for (all) flags. And if a flag is something like there is no sensible default, check the value at the start of your application and halt with an error message. You should do flag value validation anyway (not just for required flags), so this shouldn't mean any (big) overhead, and this is a good practice in general.

答案2

得分: 20

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

然而,如果您有一个不合理的情况,比如您想以任意顺序接受几个整数标志,其中任何整数值都是合理的,但没有默认值。那么,您可以使用flag.Visit函数来检查您关心的标志是否被显式设置。我认为这是唯一的方法来判断标志是否被显式设置为其默认值(不计算保持状态的自定义flag.Value类型的Set实现)。

例如,可能是这样的:

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] {
        // 或者可能使用`log.Fatalf`而不是:
        fmt.Fprintf(os.Stderr, "缺少必需的-%s参数/标志\n", req)
        os.Exit(2) // 与flag.Parse使用相同的退出代码
    }
}

Playground

如果未显式设置“-b”或“-s”标志,将产生错误。

英文:

As already mentioned, the flag package does not provide this feature directly and usually you can (and should) be able to provide a sensible default. For cases where you only need a small number of explicit arguments (e.g. an input and output filename) you could use positional arguments (e.g. after flag.Parse() check that flag.NArg()==2 and then input, output := flag.Arg(0), flag.Arg(1)).

If however, you have a case where this isn't sensible; say a few integer flags you want to accept in any order, where any integer value is reasonable, but no default is. Then you can use the flag.Visit function to check if the flags you care about were explicitly set or not. I think this is the only way to tell if a flag was explicitly set to it's default value (not counting a custom flag.Value type with a Set implementation that kept state).

For example, perhaps something like:

	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
		}
	}

<kbd>Playground</kbd>

This would produce an error if either the "-b" or "-s" flag was not explicitly set.

答案3

得分: 11

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)
英文:

go-flags lets you declare both required flags and required positional arguments:

var opts struct {
	Flag string `short:&quot;f&quot; required:&quot;true&quot; name:&quot;a flag&quot;`
	Args struct {
		First   string `positional-arg-name:&quot;first arg&quot;`
		Sencond string `positional-arg-name:&quot;second arg&quot;`
	} `positional-args:&quot;true&quot; required:&quot;2&quot;`
}
args, err := flags.Parse(&amp;opts)

答案4

得分: 10

我喜欢使用github.com/jessevdk/go-flags包来进行命令行界面(CLI)开发。它提供了一个required属性,用于设置必需的标志:

var opts struct {
...
    // 必需标志的示例
    Name string `short:"n" long:"name" description:"A name" required:"true"`
...
}
英文:

I like github.com/jessevdk/go-flags package to use in CLI. It provides a required attribute, to set a mandatory flag:

var opts struct {
...
    // Example of a required flag
    Name string `short:&quot;n&quot; long:&quot;name&quot; description:&quot;A name&quot; required:&quot;true&quot;`
...
}

答案5

得分: 8

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

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

If you have flag path, simply check if *path contains some value

var path = flag.String(&quot;f&quot;, &quot;&quot;, &quot;/path/to/access.log&quot;)
flag.Parse()
if *path == &quot;&quot; {
    usage()
    os.Exit(1)
}

答案6

得分: 6

我同意这个解决方案,但在我的情况下,默认值通常是环境值。例如,

dsn := flag.String("dsn", os.Getenv("MYSQL_DSN"), "数据源名称")

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

所以通过一些小的修改,它适用于我的情况。

使用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] {
		// 或者可能使用`log.Fatalf`代替:
		fmt.Fprintf(os.Stderr, "缺少必需的-%s参数/标志\n", req)
		os.Exit(2) // 与flag.Parse使用相同的退出代码
	}
}

在playground中测试示例

英文:

I agree with this solution but, in my case default values are usually environment values. For example,

dsn := flag.String(&quot;dsn&quot;, os.Getenv(&quot;MYSQL_DSN&quot;), &quot;data source name&quot;)

And in this case, I want to check if the values are set from invocation (usually local development) or environment var (prod environment).

So with some minor modifications, it worked for my case.

Using flag.VisitAll to check the value of all flags.

required := []string{&quot;b&quot;, &quot;s&quot;}
flag.Parse()

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

<kbd>Test example in plauground</kbd>

答案7

得分: 1

这是一个完整的工作示例。

解决UsageDefValue属性的问题,并将你的标志包装到一个结构体中。

package main

import (
	"flag"
	"fmt"
	"os"
)

type CliFlag struct {
	Required bool
	Usage    string
	Name     string
	Address  *string
}

func init() {
	flags := [...]CliFlag{
		{
			Required: true,
			Usage:    "(github.com) repository URI",
			Name:     "repo-uri",
			Address:  nil,
		},
		{
			Required: true,
			Usage:    "(Zombro) repository workspace",
			Name:     "repo-workspace",
			Address:  nil,
		},
		{
			Required: true,
			Usage:    "(zzio) repository slug",
			Name:     "repo-slug",
			Address:  nil,
		},
	}
	for i, f := range flags {
		f.Address = flag.String(f.Name, "", f.Usage)
		flags[i] = f
	}
	flag.Parse()
	missing := make([]string, 0)
	for _, f := range flags {
		if *f.Address == "" && f.Required {
			missing = append(missing, f.Name)
		}
	}
	if len(missing) > 0 {
		fmt.Printf("missing required flags: %v \n", missing)
		flag.Usage()
		os.Exit(1)
	}
}

func main() {
	fmt.Println("main")
}

缺少参数

$ go run . -repo-slug test                  
missing required flags: [repo-uri repo-workspace] 
Usage of /var/folders/mg/86n5kszs27bdqj0fpswvr0m00000gn/T/go-build2061541798/b001/exe/zzio:
  -repo-slug string
    	(zzio) repository slug
  -repo-uri string
    	(github.com) repository URI
  -repo-workspace string
    	(Zombro) repository workspace
exit status 1
英文:

Here's a full working example.

Work around the Usage and DefValue attributes, and wrap your flags into a struct.

package main

import (
	&quot;flag&quot;
	&quot;fmt&quot;
	&quot;os&quot;
)

type CliFlag struct {
	Required bool
	Usage    string
	Name     string
	Address  *string
}

func init() {
	flags := [...]CliFlag{
		{
			Required: true,
			Usage:    &quot;(github.com) repository URI&quot;,
			Name:     &quot;repo-uri&quot;,
			Address:  nil,
		},
		{
			Required: true,
			Usage:    &quot;(Zombro) repository workspace&quot;,
			Name:     &quot;repo-workspace&quot;,
			Address:  nil,
		},
		{
			Required: true,
			Usage:    &quot;(zzio) repository slug&quot;,
			Name:     &quot;repo-slug&quot;,
			Address:  nil,
		},
	}
	for i, f := range flags {
		f.Address = flag.String(f.Name, &quot;&quot;, f.Usage)
		flags[i] = f
	}
	flag.Parse()
	missing := make([]string, 0)
	for _, f := range flags {
		if *f.Address == &quot;&quot; &amp;&amp; f.Required {
			missing = append(missing, f.Name)
		}
	}
	if len(missing) &gt; 0 {
		fmt.Printf(&quot;missing required flags: %v \n&quot;, missing)
		flag.Usage()
		os.Exit(1)
	}
}

func main() {
	fmt.Println(&quot;main&quot;)
}

missing

$ go run . -repo-slug test                  
missing required flags: [repo-uri repo-workspace] 
Usage of /var/folders/mg/86n5kszs27bdqj0fpswvr0m00000gn/T/go-build2061541798/b001/exe/zzio:
  -repo-slug string
    	(zzio) repository slug
  -repo-uri string
    	(github.com) repository URI
  -repo-workspace string
    	(Zombro) repository workspace
exit status 1

答案8

得分: 0

需要注意的是,当你这样做时:

password := flag.String("password", "", "the password")
flag.Parse()

默认值是由flag.String设置的,而不是flag.Parse

所以,如果你这样做:

const unspecified = "\x00"
password := flag.String("password", "", "the password")
*password = unspecified
flag.Parse()

那么如果你在命令行中没有明确指定它,*password == unspecified。这是我在处理字符串时想要区分“空”和“未指定”的方法。

英文:

A thing to note is that when you do:

password := flag.String(&quot;password&quot;, &quot;&quot;, &quot;the password&quot;)
flag.Parse()

The default is set by flag.String, not flag.Parse.

So if you instead do:

const unspecified = &quot;\x00&quot;
password := flag.String(&quot;password&quot;, &quot;&quot;, &quot;the password&quot;)
*password = unspecified
flag.Parse()

Then *password == unspecified if you don't specify it explicitly in the command line. This is my go to for strings when I want to distinguish "empty" from "unspecified".

答案9

得分: -1

或者你可以使用docopt,只需要编写“usage”文本。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)
}
英文:

Or you could docopt, where you only have to write the "usage" text. Docopt interprets the usage text and creates an argument map. This opens up a whole lot of possibilities, all following the POSIX usage text standard. This library is available for about 20 languages already.

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

package main

import (
        &quot;fmt&quot;
        &quot;github.com/docopt/docopt-go&quot;
)

const (
        Usage = `Naval Fate.

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

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

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

huangapple
  • 本文由 发表于 2015年8月3日 19:36:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/31786215.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定