フラグパッケージを使用して、文字列フラグが渡されたかどうかを区別する良い方法はありますか?
たとえば、フラグが渡されない場合、動的なデフォルト値に設定したいと思います。ただし、""
の値でフラグが指定されている場合は空に設定します。
現在、私は次のことをしています:
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 のFlagsetには、宣言されたフラグ(「正式」)と実際に設定されたフラグ(「実際」)を含む内部マップがあります。前者は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は「フォーマル」でも同じことを行います)。
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フラグを使用した複雑なケースもあります。この場合、計算されたホストFlag()は機能しません。 「stringFlag struct型」ソリューションも、デフォルト値のアイデアを台無しにするため、最適ではありません。
この方法でそれを解決します:解析後に異なるデフォルト値で2セットのフラグを作成します-ちょうどチェック-最初のフラグセットのフラグが2番目のフラグセットのフラグと同じ値を持っている場合-フラグ値がコマンドからユーザーによって提供されたことを意味しますライン。それらが異なる場合-これは、フラグがデフォルトで設定されたことを意味します。
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 、実際の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
}