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)
fmt.Println(os.Args[0])
fmt.Println(os.Args[1])
fmt.Println(os.Args[2])
}
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() {
b := flag.Bool("force", false, "無理やり何かする")
flag.Parse()
fmt.Println(*b)
}
実行結果
twinbird@:~/go/src/flag_sample$ ./flag_sample
false
twinbird@:~/go/src/flag_sample$ ./flag_sample force=true
false
twinbird@:~/go/src/flag_sample$ ./flag_sample -force=true
true
twinbird@:~/go/src/flag_sample$ ./flag_sample --force=true
true
twinbird@:~/go/src/flag_sample$ ./flag_sample ---force=true
bad flag syntax: ---force=true
Usage of ./flag_sample:
-force
無理やり何かする
twinbird@:~/go/src/flag_sample$ ./flag_sample -force true
true
twinbird@twinbird-pc:~/Dropbox/go/src/flag_sample$ ./flag_sample -h
Usage of ./flag_sample:
-force
無理やり何かする
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
times: 10
category:
force: true
times: 10
FlagSet
カスタマイズしたフラグを作ったりするのに使えそうです.
公式を元に調べて別記事にしないとダメですね.
今回使ってません.
まとめ
これだけ機能があれば標準でも十分な気がします.