Ответ 1
Используйте функцию flag.Visit()
:
func isFlagPassed(name string) bool {
found := false
flag.Visit(func(f *flag.Flag) {
if f.Name == name {
found = true
}
})
return found
}
С пакетом флагов, есть ли хороший способ отличить, прошел ли флаг строки?
Например, когда флаг не передан, я хочу установить его в динамическое значение по умолчанию. Однако я хочу, чтобы он был пустым, если флаг был предоставлен, но со значением ""
.
Current Я делаю следующее:
flagHost = flag.String(flagHostFlagKey, "", "...")
...
setHostname := false
for _, arg := range os.Args {
if arg == "-"+flagHostFlagKey {
setHostname = true
}
}
if !setHostname {
...
Кажется, что все работает нормально, но вроде уродливо. Есть ли лучший способ во время пребывания в стандартном пакете флагов?
Используйте функцию flag.Visit()
:
func isFlagPassed(name string) bool {
found := false
flag.Visit(func(f *flag.Flag) {
if f.Name == name {
found = true
}
})
return found
}
Встроенные типы флагов не поддерживают выделение значений по умолчанию и явное присвоение значения по умолчанию. Однако пакет 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
Проблема с использованием настраиваемого типа флага (пример 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")
}
Чтобы использовать динамическое значение по умолчанию для флага, создайте флаг с установленным по умолчанию значением динамического значения:
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.
...
}
При таком подходе нет необходимости определять, указан ли флаг в командной строке. Кроме того, справка показывает правильное значение по умолчанию.
Если вычисленное значение зависит от других флагов или дорого рассчитать, используйте подход, предложенный в одном из других ответов.
Сталкиваются с той же проблемой, но имеют даже сложный случай с флагом 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
Я думаю, что более надежный способ - проверить, имеет ли какой-либо флаг в параметрах командной строки (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
}
Я обнаружил, что у нас есть метод Lookup():
func isFlagPassed(name string) bool {
rs := flag.Lookup(name)
return rs != nil
}
Полный документ