💻

標準 flag パッケージを pflag パッケージに置き換える

2022/10/08に公開

いつもの小ネタです。

標準 flag パッケージを pflag パッケージに置き換える

コマンドライン引数を評価する Go 標準の flag はシンプルながらとてもよく出来ているのだけど GNU 拡張のシンタックスが使えたらなぁ,と思ったことはありません?

たとえば --foo というパラメータの短縮形として -f が使いたいとか -a -b -c をまとめて -abc と指定したいとか。

これを実装できるのが github.com/spf13/pflag パッケージ。こんな感じに書ける。

sample1.go
package main

import (
    "fmt"

    "github.com/spf13/pflag"
)

func main() {
    f := pflag.BoolP("foo", "f", false, "option foo")
    b := pflag.BoolP("bar", "b", false, "option bar")
    pflag.Parse()

    fmt.Println("foo = ", *f)
    fmt.Println("bar = ", *b)
}

これを実行すると

$ go run sample1.go
foo =  false
bar =  false

$ go run sample1.go --foo --bar
foo =  true
bar =  true

$ go run sample1.go --foo=true
foo =  true
bar =  false

$ go run sample1.go -fb
foo =  true
bar =  true

てな感じにコマンドライン引数を指定できる。

pflag は標準 flag と互換性があるので

sample2.go
package main

import (
    "fmt"

    flag "github.com/spf13/pflag"
)

func main() {
    f := flag.Bool("foo", false, "option foo")
    b := flag.Bool("bar", false, "option bar")
    flag.Parse()

    fmt.Println("foo = ", *f)
    fmt.Println("bar = ", *b)
}

などと書くことで置き換え可能である。ただし挙動は pflag の仕様に従うので

$ go run sample2.go --foo
foo =  true
bar =  false

$ go run sample2.go -foo
unknown shorthand flag: 'f' in -foo
Usage of /tmp/go-build421334830/b001/exe/sample2:
      --bar   option bar
      --foo   option foo
unknown shorthand flag: 'f' in -foo

上のように引数に -foo とかしても「そんなもん知らん」と怒られる(笑)

【おまけ】 go test に独自のコマンドライン・パラメータを設定する

恥ずかしながら,『Go言語による分散サービス』読書会で標準 flag パッケージを使って go test に独自のコマンドライン・パラメータを設定できることをはじめて知った。こんな感じに書ける。

sample3_test.go
package sample3

import (
    "flag"
    "testing"
)

var foo = flag.Bool("foo", false, "option foo")

func TestMain(m *testing.M) {
    flag.Parse()
    m.Run()
}

func TestFlag(t *testing.T) {
    if !*foo {
        t.Errorf("option foo = %v, want %v.", *foo, true)
    }
}

これを実行すると

$ go test --shuffle on
-test.shuffle 1665227879001765953
--- FAIL: TestFlag (0.00s)
    sample3_test.go:17: option foo = false, want true.
FAIL
exit status 1
FAIL	pflag-sample/sample3.go	0.001s

$ go test --shuffle on --foo
-test.shuffle 1665227866801533228
PASS
ok  	pflag-sample/sample3.go	0.001s

となる。指定したフラグはパッケージ内でのみ有効である点に注意。

まぁ,コマンドライン引数でテスト条件を変えるというのはあまりしないだろうが,『Go言語による分散サービス』6章のサンプルコードでは --debug フラグを設定し,コマンドラインでこれが指定されている場合はトレースログを吐くようにしていて「なるほど」と思った。

これを pflag でもできないかなぁ,と思ったのだが上手くいかなかった。残念。 go test では標準 flag を使いましょう。

GitHubで編集を提案

Discussion