检查Go中是否提供了Flag

时间:2016-03-05 02:25:22

标签: go command-line-arguments

使用flag包,是否有一种很好的方法来区分是否传递了字符串标志?

例如,当没有传递标志时,我想将其设置为动态默认值。但是,如果提供了标志,但我想将其设置为空,但值为""

目前我正在做以下事情:

flagHost = flag.String(flagHostFlagKey, "", "...")
...
setHostname := false
for _, arg := range os.Args {
    if arg == "-"+flagHostFlagKey {
        setHostname = true
    }
}

if !setHostname {
     ...

这似乎工作正常,但有点难看。使用标准标志包时有更好的方法吗?

9 个答案:

答案 0 :(得分:22)

内置标记类型不支持区分默认值和显式赋值为默认值。但是,flag包非常灵活,允许您使用flag.Value界面滚动自己的类型。

这是一个完整的示例,其中包含一个字符串标记,用于记录是否已分配给它。

package main

import (
    "flag"
    "fmt"
)

type stringFlag struct {
    set   bool
    value string
}

func (sf *stringFlag) Set(x string) error {
    sf.value = x
    sf.set = true
    return nil
}

func (sf *stringFlag) String() string {
    return sf.value
}

var filename stringFlag

func init() {
    flag.Var(&filename, "filename", "the filename")
}

func main() {
    flag.Parse()
    if !filename.set {
        fmt.Println("--filename not set")
    } else {
        fmt.Printf("--filename set to %q\n", filename.value)
    }
}

这里有一些例子:

$ go run a.go -filename=abc
--filename set to "abc"

$ go run a.go -filename=
--filename set to ""

$ go run a.go
--filename not set

答案 1 :(得分:13)

使用自定义标志类型(此线程中的stringFlag示例)的问题是您稍微打乱了PrintDefaults输出(即--help)。例如,使用字符串username标志和stringFlag servername标志, - help如下所示:

-server value
    server:port (default localhost:1234)
-username string
    username (default "kimmi")

请注意,就用户而言,这些都是字符串参数,但不同之处在于stringFlag不是字符串。

flag标记集的内部地图包含已声明的标记('正式')和实际设置的标记('实际')。前者可以通过Lookup()获得,虽然后者没有暴露,或者你可以写:

var servername = flag.String("server", "localhost:8129", "server:port")

flag.Parse()

if f := flag.CommandLine.LookupActual("server"); f != nil {
    fmt.Printf("server set to %#v\n", f)
} else {
    fmt.Printf("server not set\n")
}

似乎你能做的最好,如果你想要一致的PrintDefaults()输出,就是使用Visit提取自己的“实际”视图。 (VisitAll对' formal')做同样的事情:

var servername = flag.String("server", "localhost:8129", "server:port")

flag.Parse()

flagset := make(map[string]bool)
flag.Visit(func(f *flag.Flag) { flagset[f.Name]=true } )

if flagset["server"] {
    fmt.Printf("server set via flags\n")
} else {
    fmt.Printf("server not explicitly set, using default\n")
}

答案 2 :(得分:4)

使用flag.Visit()函数:

func isFlagPassed(name string) bool {
    found := false
    flag.Visit(func(f *flag.Flag) {
        if f.Name == name {
            found = true
        }
    })
    return found
}

答案 3 :(得分:3)

要为标志使用动态默认值,请使用默认设置为动态值创建标志:

func main() {
  flagHost = flag.String(flagHostFlagKey, computedHostFlag(), "...")
  flag.Parse()
  // *flagHost equals the return value from computedHostFlag() if 
  // the flag is not specified on the command line.
  ...
}

使用这种方法,不必检测命令行上是否指定了标志。此外,帮助显示正确的默认值。

如果计算值取决于其他标志或计算成本高,则使用其他答案中建议的方法。

答案 4 :(得分:1)

面对同样的问题,但是带有bool标志的情况甚至很复杂,在这种情况下,computeHostFlag()无法正常工作,因为您可以提供仅创建true或false的标志。 “ type stringFlag struct”类型的解决方案也不是最好的,因为破坏了默认值的想法。

以这种方式解决:解析后创建两组具有不同默认值的标志-只需检查-第一个标志集中的标志与第二个标志集中的标志是否具有相同的值-这意味着该标志值由用户从命令行。如果它们不同-则表示默认情况下设置了该标志。

package main

import (
    "fmt"
    "flag"
)

func main() {
    args := []string{"-foo="}

    flagSet1 := flag.NewFlagSet("flagSet1", flag.ContinueOnError)
    foo1 := flagSet1.String("foo", "-", ``)
    boolFoo1 := flagSet1.Bool("boolfoo", false, ``)
    flagSet1.Parse(args)

    flagSet2 := flag.NewFlagSet("flagSet2", flag.ContinueOnError)
    foo2 := flagSet2.String("foo", "+", ``)
    boolFoo2 := flagSet2.Bool("boolfoo", true, ``)
    flagSet2.Parse(args)

    if *foo1 != *foo2 {
        fmt.Println("foo flag set by default")
    } else {
        fmt.Println("foo flag provided by user")
    }

    if *boolFoo1 != *boolFoo2 {
        fmt.Println("boolfoo flag set by default")
    } else {
        fmt.Println("boolfoo flag provided by user")
    }
}

游乐场:https://play.golang.org/p/BVceE_pN5PO,对于真正的CLI执行,您可以执行以下操作:https://play.golang.org/p/WNvDaaPj585

答案 5 :(得分:0)

我认为一种更可靠的方法是检查命令行参数(os.Args [1:])中是否有任何前缀带有“ prefix” + str的前缀,因此该功能:

func isInSlice(str string, list []string, prefix string) bool {
    for _, v := range list {
        if strings.HasPrefix(v, prefix + str) {
            return true
        }
    }
    return false
}

答案 6 :(得分:0)

FlagSet在我的环境中( LookupActual(),并且内部映射 actual Ben L's answer中提到的内容无法直接访问。

我有一种方法来检查是否使用reflect设置了标志:

import "reflect"

fs := flag.NewFlagSet("the flags", flag.ExitOnError)
flag_name := "host"
host := fs.String(flag_name, "localhost", "specify the host address")
// other flags
fs.Parse(os.Args[1:])

if reflect.Indirect(reflect.ValueOf(fs)).FieldByName("actual").MapIndex(reflect.ValueOf(flag_name)).IsValid() {
     fmt.Printf("the host flag is set with value %v", *host)
} else {
     fmt.Printf("the host flag is not set")
}

答案 7 :(得分:0)

https://stackoverflow.com/a/35809400/3567989相同,但具有指向字符串的指针而不是自定义结构。 Container ( width: MediaQuery .of(context) .size .width, child: Image.network(my_image_name, fit: BoxFit.fitWidth ) ) 如果未设置则为nil,如果未设置则为nil。

*string

答案 8 :(得分:-1)

我发现我们有Lookup()方法:

func isFlagPassed(name string) bool {
  rs := flag.Lookup(name)
  return rs != nil 
}

完整docs