はじめに
この記事は Go Advent Calendar 1日目の記事です。
go でサクッとコマンドラインが作りたい
最近、よく行う処理を PHP でスクリプト化しています。
例えば、以下のような処理です。
- CSVを突き合わせて差分を出す
- 動作確認のために開発環境のAPIを叩く
- etc...
しかし、PHPの実行環境がないといけない、、PHPのバージョンが違うと動かないなど、不便な点もあります。
そこで、 go で実装して、ビルドしてバイナリさえ用意しておけば、だいたいどんな環境でも動きます。この点が楽だなと思って、このスクリプトを go で書くことにしました。
CLI アプリケーションライブラリ Cobra
コマンドの引数などのパースをするために、 Cobra を導入しました。
コマンドの構成
今回は例として、2つのコマンドを実装することを考えます。
- CSVを突き合わせて差分を出す
- 動作確認のために開発環境のAPIを叩く
最終的な成果物として、2パターンの構成が考えられます。
- バイナリは1つで、引数で処理を分岐させる
- 例: バイナリとして
utakata
というファイルを出力する ./utakata compare-csv
: CSVを突き合わせて差分を出す./utakata test-api
: 開発環境のAPIを叩く
- 例: バイナリとして
- バイナリを2つ出力する
./compare-csv
: CSVを突き合わせて差分を出す./test-api
: 開発環境のAPIを叩く
ビルドや実装が楽なので、引数で処理を分岐させることにしました。
ディレクトリ構成
Go の標準ディレクトリ構成には /cmd
というのがあり、今回はこの /cmd
以下にいっぱいファイルを突っ込んでしまったのですが、 本来の /cmd
ディレクトリの用途とは違うので、多分別の名前がいいです。
/commands
とか /services
とかがいいかも。
Go のディレクトリ構成についてはこれを読んでみてください。
https://github.com/golang-standards/project-layout/blob/master/README_ja.md
ディレクトリ構成はこのようになっています
go.mod go.sum main.go cmd/ compare_csv.go test_api.go
Cobra の基本的な使い方
compare_csv.go
の例
package cmd import ( "fmt" "github.com/spf13/cobra" ) // CSVを比較します func CompareCSV() *cobra.Command { // コマンドの定義 command := &cobra.Command{ Use: "compare-csv", Short: "2つのCSVを比較する", } // 引数 command.Flags().StringP( "file-a", // 長い引数 "a", // 短い引数 "", // デフォルト値 "比較元ファイル", command.Flags().StringP( "file-b", "b", "", "比較先ファイル", ) // コマンド本体 command.RunE = func(cmd *cobra.Command, args []string) error { fileA, err := cmd.Flags().GetString("file-a") if err != nil { return err } fileB, err := cmd.Flags().GetString("file-b") if err != nil { return err } // 以下略 } return command }
Cobra を使うことで、引数だったりオプションだったりを簡単に設定できます。
utakata compare-csv --file-a hoge.csv --file-b fuga.csv
のように使えます。
main.go
はこんな感じ
package main import ( "fmt" "github.com/spf13/cobra" "os" "utakata/cmd" ) func main() { rootCmd := &cobra.Command{ Use: "utakata", Short: "便利スクリプト", Long: `プレミアム課金で役に立つスクリプト`, } rootCmd.AddCommand(cmd.CompareCSV()) rootCmd.AddCommand(cmd.TestAPI()) err = rootCmd.Execute() if err != nil { fmt.Println(err.Error()) os.Exit(1) } os.Exit(0) }
func TestAPI()
の方は割愛します。
一応、go.mod
はこんな感じ
module utakata go 1.14 require ( github.com/joho/godotenv v1.4.0 )
これでビルドします。
go build main.go
すると、 utakata
というバイナリが出力されるので、あとは実行するだけです。
./utakata compare-csv ... ./utakata test-api
まとめ
Cobra を使うと、簡単にコマンドラインツールが作れるので、試してみたらどうでしょう。
ある程度ならシェルスクリプトでもできますが、DBからデータ持ってきて加工するとか、goのほうが楽な場合もあります。
おまけ: その他におすすめのライブラリ
godotenv というライブラリが個人的に便利でおすすめです。
こちらですが、.env
ファイルを用意しておき、
DB_HOST=127.0.0.1 DB_USER=test DB_PASSWORD=password
godotenv.Load()
すると、いつもどおり os.Getenv
で .env
の内容が取れるようになるというものです。
package main import ( "github.com/joho/godotenv" ) func main() { err := godotenv.Load() if err != nil { fmt.Println("環境変数が読み込めませんでした。", err) os.Exit(1) } host := os.Getenv("DB_HOST") }
おすすめで愛用しているので、気になっている人は使ってみてください。