【Golang】ビルドしたバイナリのバージョン情報を表示、コマンドラインオプションを受け取るには (flag, spf13/pflag パッケージ)

本番環境にデプロイしたビルド済のバイナリのバージョンを確認したいことがあります。 また、開発環境で使うちょっとしたツールを作る場合、ほとんどの場合いくつかのコマンドラインオプションを受け取れるようにしたいです。

これらのバージョン表示、コマンドラインオプションは共に

で実現できます。

しかし、サードパーティ製ライブラリを含めると種類が多いため、どれを使って良いか分かりづらいです。それぞれの特徴を見て、どのような時にどの方法で実現したら良いか紹介してみようと思います。

結論 (どのような時にどの方法で実現したら良いか)

結論としては

  • バージョン情報のみを表示したい場合
    • 標準の flagパッケージ
  • 社内でのみ利用するツールやコマンドラインオプションが少ない場合
    • 標準の flagパッケージ
  • 一般に公開するツールやコマンドラインオプションが多い場合

になるかと思います。

それぞれ特徴を見ていきます。

標準の flagパッケージ

特徴

特徴としては

  • 標準パッケージのためライブラリ単体のバージョン更新は不要
  • ロングオプションの形式が、GNU/POSIXコマンドラインツールと異なる
    • 例えば、ロングオプション version、ショートオプション vを追加する場合
      • ロングオプションの形式は -version になる。 (GNU/POSIX--version を推奨。こちらは-が2つ。)
      • ショートプションの形式は -v になる。
  • ロングオプションとショートオプションの両方を追加するのが煩雑

また、標準の flag パッケージを使う場合

の間に実装方法に差異はありません。

コード

コードを書いて確認していきたいと思います。ショートオプションとロングオプションの両方を追加したい場合、2行に分けて書く必要があります。

package main

import (
    "flag"
    "fmt"
)

var version = "1.0"

func main() {
    var withVersion bool

    // ショートオプション `-v` を追加(デフォルトはflase)
    flag.BoolVar(&withVersion, "v", false, "version: short option")

    // ロングオプション `-version` を追加(デフォルトはflase)
    flag.BoolVar(&withVersion, "version", false, "version: long option")

    // 指定されたオプションを読み込む
    flag.Parse()

    // versionオプションが指定されていれば標準出力して戻る
    if withVersion {
        fmt.Println("Version ", version)
        return
    }

    // オプションversionが指定されていない場合の処理
    // 〜〜〜
}

追加オプションをつけて、go run で実行すると、バージョン情報が表示されることを確認できます。

$ go run main.go  -v
Version  1.0
$ go run main.go  -version
Version  1.0

ちなみに、flag.Parse() を実行することで、-h-helpも使えるようになります。

$ go run main.go  -h
Usage of /tmp/go-build277262992/b001/exe/main:
  -v    version: short option
  -version
        version: long option

用途

という特徴から

  • バージョン情報のみを表示したい
  • 社内でのみ利用するツールやコマンドラインオプションが少ない

といったケースでは、標準の flagパッケージを使うのが良いと言えそうです。

サードパーティー製のパッケージ

flagパッケージの代替となるサードパーティー製のパッケージには様々なものがありますが、2021年2月現在 spf13/pflag がよく使われてます。

よく使われる理由として考えられるのは、 spf13/cobra というコマンドラインツールのフレームワークで使われているライブラリだからです。

cobra

  • github cli
  • docker cli
  • kubectl

等の様々なコマンドラインツールを実装するのに使われている人気のライブラリなので、長期的にメンテナンスされることが見込まれている、という点が大きいかと思います。

参考までに、よく使われている他のサードパーティ製パッケージと併せて

  • go.modで requireされている数
  • Githubのスターの数

を記載しておきます。

spf13/pflag (サードパーティ製パッケージ)

特徴

特徴としては

  • オプションの形式が、GNU/POSIXコマンドラインツールと同じになる
    • 例えば、ロングオプション version、ショートオプション vを追加する場合
      • ロングオプションの形式は --version になる。
      • ショートプションの形式は -v になる。
  • ロングオプションとショートオプションの両方を追加するのが容易
  • サードパーティのパッケージのためライブラリ単体でバージョンの更新が必要

になるかと思います。

コード

こちらもコードを書いて確認してみます。ショートオプションとロングオプションの両方を追加したい場合、1行で表現できるのが確認できます。複数のオプションが必要な場合はこちらの方法が便利です。

package main

import (
    "fmt"
    "github.com/spf13/pflag"
)

var version = "1.0"

func main() {
    var withVersion bool

    // ロングオプション `--version`、ショートオプション `-v`を追加
    // デフォルトはflase
    pflag.BoolVarP(&withVersion, "version", "v", false, "version")

    // 指定されたオプションを読み込む
    pflag.Parse()

    // versionオプションが指定されていれば標準出力して戻る
    if withVersion {
        fmt.Println("Version ", version)
        return
    }

    // オプションversionが指定されていない場合の処理
    // 〜〜〜
}

オプションをつけて、go run で実行すると、バージョン情報が表示されることを確認できます。

$ go run main.go  -v
Version  1.0
$ go run main.go  --version
Version  1.0

なお、spf13/pflagで何らかのオプションを追加する方法を紹介するために version オプションを追加するコードを載せていますが、コマンドラインフレームワークであるcobraと一緒に使う場合は、versionオプションは追加不要です。(参照)

こちらのサードパーティーのパッケージも、pflag.Parse() を実行することで、-h-helpも使えるようになります。ヘルプの表示フォーマットに少し違いがあり、ショートオプションとロングオプションは1行で表示されます。

$ go run main.go -h
Usage of /tmp/go-build962608044/b001/exe/main:
  -v, --version   version

用途

  • オプションの形式が、GNU/POSIXコマンドラインツールと同じになる
  • ロングオプションとショートオプションの両方を追加するのが容易

という特徴から

といったケースでは、spf13/pflag のようなサードパーティのパッケージを使うのが良いと言えそうです。

便利機能

他にも spf13/pflagには便利な機能があります。長期的に運用していく時に必要な機能になりそうです。

関連記事

今回はspf13/pflagに関する記事でしたが、コマンドラインフレームワークspf13/cobraにも興味持たれた方は、もしよろしければ以下の記事も参照してみてください。 simple-minds-think-alike.hatenablog.com

simple-minds-think-alike.hatenablog.com

参考情報