Проверьте, предоставлен ли флаг в Go

С пакетом флагов, есть ли хороший способ отличить, прошел ли флаг строки?

Например, когда флаг не передан, я хочу установить его в динамическое значение по умолчанию. Однако я хочу, чтобы он был пустым, если флаг был предоставлен, но со значением "".

Current Я делаю следующее:

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

if !setHostname {
     ...

Кажется, что все работает нормально, но вроде уродливо. Есть ли лучший способ во время пребывания в стандартном пакете флагов?

Ответы

Ответ 1

Используйте функцию flag.Visit():

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

Ответ 2

Встроенные типы флагов не поддерживают выделение значений по умолчанию и явное присвоение значения по умолчанию. Однако пакет 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

Ответ 3

Проблема с использованием настраиваемого типа флага (пример stringFlag в этом потоке) заключается в том, что вы слегка нарушите вывод PrintDefaults (т.е. --help). Например, с флагом имени пользователя строки и флагом 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() использовался, чтобы использовать "Визит", чтобы извлечь свой собственный взгляд на "фактический" (VisitAll делает то же самое с "формальным" ):

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

Ответ 4

Чтобы использовать динамическое значение по умолчанию для флага, создайте флаг с установленным по умолчанию значением динамического значения:

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

При таком подходе нет необходимости определять, указан ли флаг в командной строке. Кроме того, справка показывает правильное значение по умолчанию.

Если вычисленное значение зависит от других флагов или дорого рассчитать, используйте подход, предложенный в одном из других ответов.

Ответ 5

Сталкиваются с той же проблемой, но имеют даже сложный случай с флагом bool, в этом случае computedHostFlag() не работает, так как вы можете предоставить для создания флага только true или false. "Тип 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

Ответ 6

Я думаю, что более надежный способ - проверить, имеет ли какой-либо флаг в параметрах командной строки (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
}

Ответ 7

Я обнаружил, что у нас есть метод Lookup():

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

Полный документ