search
LoginSignup
92

More than 5 years have passed since last update.

posted at

Goでflagを使ってコマンドライン引数を扱う

多分一億番煎じくらいの内容:tea:
記事としての価値はまあ置いといて:raised_hand:
せっかくやったので整理のために書きます:bulb:

公式ドキュメント
https://golang.org/pkg/flag/

非フラグの取得

Parse()を呼んだ後にArgs()[]stringとして取得できます。

sample1_1.go
package main

import (
    "flag"
    "fmt"
)

func main() {
    flag.Parse()
    args := flag.Args()
    fmt.Println(args)
}
実行例1-1
$ go run sample1_1.go a b c
[a b c]

$ go run sample1_1.go 1 2 3
[1 2 3]

特定の要素だけ取り出したい場合はArg(int)が使えます。こちらもstringとして取り出します。

sample1_2.go
package main

import (
    "flag"
    "fmt"
)

func main() {
    flag.Parse()
    fmt.Println(flag.Arg(0), flag.Arg(1))
}
実行例1-2-1
$ go run sample1_2.go hoge fuga
hoge fuga

範囲外アクセスしたつもりが落ちたりはしませんでした。空文字列が返ってくるっぽいです。

実行例1-2-2
$ go run sample1_2.go 1
1

フラグの取得

型名()あるいは型名Var()でフラグの定義をした後Parse()でそれぞれの変数に取得できます。
定義は(フラグ名、デフォルト値、ヘルプメッセージ)の3つからなります。
型名()を使った場合、指定した型のポインタが返ってきます。

sample2_1.go
package main

import (
    "flag"
    "fmt"
)

func main() {
    var (
        i = flag.Int("int", 0, "int flag")
        s = flag.String("str", "default", "string flag")
        b = flag.Bool("bool", false, "bool flag")
    )
    flag.Parse()
    fmt.Println(*i, *s, *b)
}
実行例2-1-1
$ go run sample2_1.go -int 2 -str hello -bool true
2 hello true

フラグが指定されない場合はデフォルト値が入ります。

実行例2-1-2
$ go run sample2_1.go
0 default false

型名Var()を使った場合、引数で渡した変数にバインドされます。

sample2_2.go
package main

import (
    "flag"
    "fmt"
    "time"
)

func main() {
    var (
        d time.Duration
        f float64
    )
    flag.DurationVar(&d, "dur", 1 * time.Second, "duration flag")
    flag.Float64Var(&f, "float", 0.1, "float flag")
    flag.Parse()
    fmt.Println(d, f)
}
実行例2-2-1
$ go run sample2_2.go -dur 1h -float 2.3
1h0m0s 2.3

パースできない値を渡すと怒られます。駄目な理由を教えてくれるので親切です。ついでにフラグの種類とデフォルト値も表示してくれます。

実行例2-2-2
$ go run sample2_2.go -float str
invalid value "str" for flag -float: strconv.ParseFloat: parsing "str": invalid syntax
Usage of (略):
  -dur duration
        duration flag (default 1s)
  -float float
        float flag (default 0.1)

定義していないフラグを渡しても怒られます。

実行例2-2-3
$ go run sample2_2.go -unkown yeah
flag provided but not defined: -unkown
(以下略)

フラグの書き方は-flag valueでも-flag=valueでも大丈夫です。ただしboolの場合は-flag=valueの形式を使ったほうが良いみたいです(下の実行例3参照)。

実行例2-2-4
$ go run sample2_2.go -dur=1m -float .5
1m0s 0.5

-hでヘルプを表示できます。

実行例2-2-5
$ go run sample2_2.go -h
Usage of (略):
  -dur duration
        duration flag (default 1s)
  -float float
        float flag (default 0.1)

個数を数える

非フラグはNArg()、フラグはNFlag()でそれぞれ個数を数えられます。

sample3.go
package main

import (
    "flag"
    "fmt"
)

func main() {
    flag.Int("int", 0, "int flag")
    flag.String("str", "default", "string flag")
    flag.Bool("bool", false, "bool flag")
    flag.Parse()
    fmt.Println(flag.NArg(), flag.NFlag())
}
実行例3
$ go run sample3.go -int 1 -str foo -bool=true a b
2 3

$ go run sample3.go -int 1 -str foo -bool true a b
3 3

$ go run sample3.go -bool true -int 1 -str foo a b
7 1

$ go run sample3.go a b c -bool=true -str foo
6 0

$ go run sample3.go -bool=true -str foo a b c 
3 2

$ go run sample3.go a b c 
3 0

$ go run sample3.go -bool=true -str foo
0 2

フラグがboolかつ後ろに引数が続く場合、-flag=valueの形式で書かないとフラグの値として認識してくれませんでした。
また、非フラグはフラグより後ろに書かないといけないみたいです。

おわりに

flag超便利だな!!!って思いました:sparkles:
今までコマンドライン引数って文字列を頑張ってパースしないといけないものだとばかり思っていたもので:sweat_smile:
言語にあらかじめこういったパッケージが用意されているのはホント素晴らしい:tada:

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
92