Golangで普通の文字列をflagで解析する
小ネタです。Goのflagパッケージを使えば、コマンドライン引数の解析を簡単にできます。プログラムに与えられた言わば本当のコマンドライン引数(つまりos.Args
)ではなく、ただのstringをコマンドライン引数として解析する方法について書きます。
結論
表題の課題は、次のようなコードで実現できます。
- main.go
package main import ( "flag" "fmt" ) func main() { f := flag.NewFlagSet("command", flag.ExitOnError) str := f.String("str", "default value", "") f.Parse([]string{"-str=aaaa"}) fmt.Println("str: ", *str) }
- 実行
$ go run main.go str: aaaa
flagパッケージを読んでみる
まず、本当のコマンドライン引数を解析する例を見てみましょう。
- main.go
package main import ( "flag" "fmt" ) func main() { str := flag.String("str", "default value", "") flag.Parse() fmt.Println("str: ", *str) }
- 実行
$ ./out str: default value $ ./out -str=aaaa str: aaaa
コマンドライン引数str
が解釈されていることがわかります。しかしよく見てみると、os.Args
を明示するようなコードは書いていませんね。ということはflagパッケージの中に、os.Args
を読んでそのstringを解釈するような処理があるはずです。
実際にflagパッケージを読んでみます。 https://golang.org/src/flag/flag.gogolang.org
まず、String
とParse
を読むと、それぞれCommandLine.String
・CommandLine.Parse
を内部で呼んでいます。
ここでCommandLine
はグローバル変数で以下のように初期化されています。
var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
さらにNewFlagSet
を追います。
func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet
NewFlagSet returns a new, empty flag set with the specified name and error handling property. If the name is not empty, it will be printed in the default usage message and in error messages.
どうやら名前とエラーハンドリングを指定したフラグセットというものを返す関数のようです。つまり変数CommandLine
はos.Args[0]
(実行ファイルの名前)が指定されることになります。
ここで再度Parse
の実装を見てみます。
// Parse parses the command-line flags from os.Args[1:]. Must be called // after all flags are defined and before flags are accessed by the program. func Parse() { // Ignore errors; CommandLine is set for ExitOnError. CommandLine.Parse(os.Args[1:]) }
https://golang.org/src/flag/flag.go?s=33956:33968#L1010
CommandLine.Parse
で呼ばれている関数は以下のように定義されています。
func (f *FlagSet) Parse(arguments []string) error
以上のことから、変数CommandLine
はos.Args[0]
を元にNewFlagSet
で初期化され、os.Args[1:]
がParse
されることがわかります。
よって、普通の文字列をflagで解析するには、NewFlagSet
で新たなフラグセットをつくり、(f *FlagSet) Parse
に解析したい[]string
を渡せばいいことがわかりました。