parser golang fortnite for flag example cli go command-line-arguments

fortnite - golang console flags



Comprobar si se proporcionó una bandera en Go (4)

El problema con el uso de un tipo de marca personalizado (el ejemplo de stringFlag en este hilo) es que alterará ligeramente la salida de PrintDefaults (es decir, --help). Por ejemplo, con una marca de nombre de usuario de cadena y una bandera de nombre de servidor de cadena de caracteres, --help se ve así:

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

Tenga en cuenta que ambos son argumentos de cadena en lo que concierne al usuario, pero que se presentan de manera diferente, ya que stringFlag no es una cadena.

flag ''s Flagset tiene un mapa interno que incluye las banderas que se declararon ("formales") y las que realmente se establecieron ("reales"). El primero está disponible a través de Lookup (), aunque desgraciadamente el último no está expuesto, o simplemente puedes escribir:

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

Parece que lo mejor que puedes hacer, si quieres una salida de PrintDefaults () consistente, es usar Visit para extraer tu propia vista de "real" (VisitAll hace lo mismo con "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") }

Con el paquete de bandera, ¿hay una buena manera de distinguir si se pasó una bandera de cadena?

Por ejemplo, cuando no se pasa el indicador, quiero establecerlo en un valor predeterminado dinámico. Sin embargo, quiero establecerlo en vacío si se proporcionó el indicador pero con un valor de "" .

Actual estoy haciendo lo siguiente:

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

Lo que parece funcionar bien, pero es algo feo. ¿Hay una mejor manera de permanecer con el paquete de bandera estándar?


Enfrente con el mismo problema, pero tenga un caso complejo con el indicador bool, en este caso computedHostFlag () no funciona, ya que puede proporcionar la creación del indicador solo verdadero o falso. La solución "type stringFlag struct" tampoco es la mejor, ya que arruina la idea de los valores predeterminados.

Resuélvalo de esta manera: cree dos conjuntos de indicadores, con diferentes valores predeterminados, después del análisis; simplemente verifique - si el indicador en el primer indicador tiene el mismo valor que el indicador del segundo conjunto, que significa que el usuario proporcionó el valor del indicador desde línea. Si son diferentes, significa que ese indicador se estableció de forma predeterminada.

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

playground: https://play.golang.org/p/BVceE_pN5PO , para una ejecución CLI real, puede hacer algo como eso: https://play.golang.org/p/WNvDaaPj585


Los tipos de indicadores incorporados no admiten la distinción entre valores predeterminados y la asignación explícita al valor predeterminado. Sin embargo, el paquete de flag es bastante flexible y le permite rodar su propio tipo que lo hace, usando la interfaz flag.Value .

Aquí hay un ejemplo completo que contiene una marca de cadena que registra si se ha asignado.

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

Aquí hay algunos ejemplos de ejecuciones:

$ 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


Para usar un valor predeterminado dinámico para una bandera, cree la bandera con el valor predeterminado establecido en el valor dinámico:

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

Con este enfoque, no es necesario detectar si el indicador está especificado en la línea de comando. Además, la ayuda muestra el valor predeterminado correcto.

Si el valor calculado depende de otros indicadores o es costoso de calcular, utilice el enfoque sugerido en una de las otras respuestas.