Is a bug: flag.Parse() could not get values after bool arg?

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

Is a bug: flag.Parse() could not get values after bool arg?

问题

为什么无法在命令行中获取以下布尔类型参数的值?以下是我的测试代码:

import (
	"flag"
	"fmt"
)

var stringVal string
var intVal int
var boolValue bool

func init() {
	flag.StringVar(&stringVal, "s", "", "string value")
	flag.BoolVar(&boolValue, "b", false, "bool value")
	flag.IntVar(&intVal, "i", -1, "int value")
}

func main() {
	flag.Parse()

	fmt.Println("stringVal:", stringVal, ", boolValue:", boolValue, ", intVal:", intVal)
}

运行方式:

  • go run flag.go -s test -b true -i 10
    得到结果:stringVal: test , boolValue: true , intVal: -1
  • go run flag.go -s test -b false -i 10
    得到结果:stringVal: test , boolValue: true , intVal: -1
  • go run flag.go -b false -s test -i 10
    得到结果:stringVal: , boolValue: true , intVal: -1
  • go run flag.go -s test -i 10 -b false
    得到结果:stringVal: test , boolValue: true , intVal: 10

Go 版本:1.16

英文:

Why could not get the values that following bool type arg in the command line?
Here's my test code:

import (
	"flag"
	"fmt"
)

var stringVal string
var intVal int
var boolValue bool

func init() {
	flag.StringVar(&stringVal, "s", "", "string value")
	flag.BoolVar(&boolValue, "b", false, "bool value")
	flag.IntVar(&intVal, "i", -1, "int value")
}

func main() {
	flag.Parse()

	fmt.Println("stringVal:", stringVal, ", boolValue:", boolValue, ", intVal:", intVal)
}

Run it as:

  • go run flag.go -s test -b true -i 10
    Got: stringVal: test , boolValue: true , intVal: -1
  • go run flag.go -s test -b false -i 10
    Got: stringVal: test , boolValue: true , intVal: -1
  • go run flag.go -b false -s test -i 10
    Got: stringVal: , boolValue: true , intVal: -1
  • go run flag.go -s test -i 10 -b false
    Got: stringVal: test , boolValue: true , intVal: 10

Go version: 1.16

答案1

得分: 6

布尔标志是根据标志的存在与否来设置的。它们通常默认为false,并且标志的存在用于修改默认行为。

例如...

  • 要显示目录的长格式列表,你使用的是ls -l,而不是ls -l true
  • 要通过交互式提示使rm更安全,以便询问是否删除,你使用的是rm -i,而不是rm -i true
  • 要使用df显示可读的输出,你使用的是df -h,而不是df -h true

你在-b之后放置的truefalse被作为参数提供给你的程序,而不是作为标志,它们的存在会中断后续的标志处理。

在你的main函数末尾添加以下内容:

fmt.Println("Remaining args", flag.Args())

假设你这样调用:go run flag.go -b false -s test -i 10,你会看到标志处理停在了false,剩余的参数作为非标志参数传递给你的程序:

$ go run flag.go -b false -s test -i 10
stringVal:  , boolValue: true , intVal: -1
Remaining args [false -s test -i 10]

顺便说一下,“布尔”标志背后的一般理念是给你的程序一些默认行为,并提供两个标志:一个修改默认行为,另一个相反的标志来取消修改,恢复默认行为。在处理标志时,最后一个标志会覆盖之前的标志。

例如,rm提供了-i来使命令交互式,并提供了-f来取消-i标志。

这样,你可以在shell中设置alias rm="rm -i",这意味着所有的rm调用都会应用-i标志以确保安全,在删除文件之前会提示你。然而,如果你想运行一个非交互式的rm,你仍然可以输入rm -f,它会扩展为rm -i -f,最后一个标志(-f)会生效。否则,你每次想要非交互式调用时都需要取消别名,运行rm,然后恢复别名。

英文:

Boolean flags are set based on the presence or absence of the flag. They typically default to false, and the presence of the flag is used to modify the default behavior.

For example...

  • to display the long listing of a directory with ls, you use ls -l, not ls -l true
  • to make rm safer by having it interactively prompt for deletes, you use rm -i, not rm -i true
  • to display human-readable output with df, you use df -h, not df -h true

The true and false you're placing after the -b are being provided to your program as arguments, not as flags, and their presence interrupts further processing of flags.

Add the following to the end of your main function:

fmt.Println("Remaining args", flag.Args())

Given an invocation such as go run flag.go -b false -s test -i 10, you'll see that flag processing stopped at false, and that the remaining arguments are passed to your program as non-flag arguments:

$ go run flag.go -b false -s test -i 10
stringVal:  , boolValue: true , intVal: -1
Remaining args [false -s test -i 10]

As an aside, the general philosophy behind "boolean" flags is that you give your program some default behavior, and provide two flags: One which modifies that default, and an opposite flag that negates the modification, restoring the default. When processing flags, the last flag will win.

For example, rm provides -i to make the command interactive, and -f to negate the -i flag.

This allows you to set alias rm="rm -i" in your shell, meaning all invocations of rm will have the -i flag applied for safety, prompting you before removing files. However, if you want to run a non-interactive rm, you can still type rm -f, which expands to be rm -i -f, and the last flag (-f) wins. Otherwise, you'd have destroy the alias, run rm and then restore the alias every time you wanted a non-interactive invocation.

答案2

得分: 3

一个布尔标志测试的是标志的存在,而不是它的值。如果你想设置一个值,请使用:

go run flag.go -s test -b=false -i 10
英文:

A boolean flag tests the existence of the flag, not the value of it. If you want to set a value, use:

go run flag.go -s test -b=false -i 10

huangapple
  • 本文由 发表于 2021年9月30日 08:42:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/69384797.html
匿名

发表评论

匿名网友

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

确定