go言語(golang)はマルチプラットフォームでバイナリを吐けるのでコマンドラインツールを作るにはちょうどいい言語だと思います.
標準パッケージも充実していて,オプションやメッセージを出力するパッケージが標準で準備されています.
当初ググると以下が人気だということだったんですが,僕は割と保守的なので標準パッケージ使います.
GitHub - tcnksm/gcli: The easy way to build Golang command-line application.
osパッケージを使う
一番基礎的な扱い方はOSパッケージを使う方法でしょう. Cを使ったことがある人なら馴染みある感じです.
サンプルソース
package main import ( "fmt" "os" ) func main() { fmt.Println(os.Args) // => [./flag_sample arg1 arg2] fmt.Println(os.Args[0]) // => ./flag_sample fmt.Println(os.Args[1]) // => arg1 fmt.Println(os.Args[2]) // => arg2 }
os.Argsにはプログラムのパス(index:0)と引数(index:1以降)が入ります.
実行結果
twinbird@:~/go/src/flag_sample$ ./flag_sample arg1 arg2 [./flag_sample arg1 arg2] ./flag_sample arg1 arg2
flagパッケージを使う
引数を扱う時にはCで言うところのgetoptsみたいなものが欲しくなると思います.
(といいつつ僕もほとんど使ったことがないですが)
そこでflagパッケージがあります.
色々面倒事を引き受けてくれる便利なものです.これが標準なんだからいいもんです.
flagパッケージで値を受け取るには2つの方法があります.
- 変数を宣言してflag.xxxVarを呼び出す
- flag.xxxを呼び出して戻り値を受け取る
(xxxはそれぞれ型名)
flag.xxxVarを使う場合
package main import ( "fmt" "flag" ) func main() { /* * 1. `force`という名前のフラグで * 2. デフォルト値は`false`. * 3. Usageとして`無理やり何かする`を表示 */ b := flag.Bool("force", false, "無理やり何かする") /* 使う前にParseしないとならない. */ flag.Parse() /* ポインタが返されるので間接参照のためにアスタリスクつける */ fmt.Println(*b) }
実行結果
twinbird@:~/go/src/flag_sample$ ./flag_sample false # => デフォルトのfalseが表示される twinbird@:~/go/src/flag_sample$ ./flag_sample force=true false # => -か--をオプションとみなすのでデフォルトのfalseが表示される(force=trueは単なる第一引数になる) twinbird@:~/go/src/flag_sample$ ./flag_sample -force=true true # => -forceオプションをtrueにしたのでtrue twinbird@:~/go/src/flag_sample$ ./flag_sample --force=true true # => --もオプションとみなすのでtrueになる twinbird@:~/go/src/flag_sample$ ./flag_sample ---force=true bad flag syntax: ---force=true Usage of ./flag_sample: -force 無理やり何かする # => ---はオプション扱いしない. # フラグ指定がおかしいのでUsageが表示される. # Usageは指定してあった`無理やり何かする`が表示される twinbird@:~/go/src/flag_sample$ ./flag_sample -force true true # => forceとtrueの間に=がなくても認識される twinbird@twinbird-pc:~/Dropbox/go/src/flag_sample$ ./flag_sample -h Usage of ./flag_sample: -force 無理やり何かする # => -hオプションをつけるとすべてのオプションのUsageを表示してくれる.
flag.xxxを使う場合
それほど変わらない.
変数を先に定義してアドレスを渡すか、ポインタを戻してもらうかの違い.
個人的にはこちらのほうが使い勝手がよい.
package main import ( "fmt" "flag" ) func main() { var b bool flag.BoolVar(&b, "force", false, "無理やり何かする") flag.Parse() fmt.Println(b) }
ヘルプ(Usage)を作る
-hでのUsageの説明文もカスタマイズすることができる.
package main import ( "fmt" "flag" "os" ) func main() { var b bool flag.Usage = func() { fmt.Fprintf(os.Stderr, "使い方:これこれこんな感じ.\n") flag.PrintDefaults() // => オプションの説明文を表示 } flag.BoolVar(&b, "force", false, "無理やり何かする") flag.Parse() fmt.Println(b) }
実行結果
twinbird@:~/go/src/flag_sample$ ./flag_sample -h 使い方:これこれこんな感じ. -force 無理やり何かする
引数の数を得る
NArg()で得ることができます.flag.Parse()の実行後出ないと出ません.
package main import ( "fmt" "flag" ) func main() { var b bool flag.BoolVar(&b, "force", false, "無理やりやる") flag.Parse() fmt.Println(flag.NArg()) }
実行結果
twinbird@:~/go/src/flag_sample$ ./flag_sample -force true arg2 2
-forceがtrueということで3つではなく2つとちゃんと認識してますね.
1つのオプションに2つの指定方法を与える
xxxVarを利用するなら単に同じ変数を与えてやればよい.
package main import ( "fmt" "flag" ) func main() { var b bool flag.BoolVar(&b, "force", false, "無理やりやる") flag.BoolVar(&b, "f", false, "無理やりやる") flag.Parse() fmt.Println(b) }
実行結果
twinbird@:~/go/src/flag_sample$ ./flag_sample false twinbird@:~/go/src/flag_sample$ ./flag_sample -f true true twinbird@:~/go/src/flag_sample$ ./flag_sample -force true true
指定されたオプションを順に処理する
visitを利用すれば良いようです.
visitAllにすれば指定されていないオプションも対象にするようです.
package main import ( "fmt" "flag" ) func main() { var b bool var s string var i int flag.BoolVar(&b, "force", false, "無理やりやる") flag.IntVar(&i, "times", 0, "繰り返す") flag.StringVar(&s, "category", "", "カテゴリー指定") flag.Parse() fmt.Println(b) fmt.Println(s) fmt.Println(i) visit_str := "" flag.Visit(func(f *flag.Flag) { visit_str += fmt.Sprintf("%s: %v\n", f.Name, f.Value) }) fmt.Println(visit_str) visitAll_str := "" flag.VisitAll(func(f *flag.Flag) { visitAll_str += fmt.Sprintf("%s: %v\n", f.Name, f.Value) }) fmt.Println(visitAll_str) }
実行結果
twinbird@:~/go/src/flag_sample$ ./flag_sample -times=10 -force=true true 10 force: true # <- ここからVisit(指定されたものだけ対象にするのでcategoryが出ない) times: 10 category: # <- ここからVisitAll(指定されていなくても対象になるのでデフォルト値が出る) force: true times: 10
FlagSet
カスタマイズしたフラグを作ったりするのに使えそうです.
公式を元に調べて別記事にしないとダメですね.
今回使ってません.
まとめ
これだけ機能があれば標準でも十分な気がします.