Golangでcliツール作るならこれで！

この記事はITC Advent Calendar(1) 24日目の記事です

その一
https://adventar.org/calendars/2563

その二
https://adventar.org/calendars/2640

初めまして、がっちゃんです
本年度のITC( https://itc.moe )の部長やってます

今回は思いつきで始めた**Golang で CLI ツールを作る。**

- 翻訳ツール https://qiita.com/ahaha0807_alg/items/83ebf6fec4790d7a6130

で使った、 [`urfave/cli`](https://github.com/urfave/cli) というパッケージについてちょこっと解説記事を書くことにしました。

## なぜ使おうと思ったか

Golang初心者だったので、検索して一発目に出てきたパッケージを使うことにした

## どんなパッケージ？

### 概要

簡単にcliツールを作ることができる、cliのフレームワークと言ってもいいほど高機能なパッケージ

### いいところ

一言で言うと、**cliツールに必要な要件がめっちゃ整理されてて使いやすい**

- helpは自動で生成される&必要なところだけ構造体に代入するだけでよしなに成形して表示してくれる
- コマンド(`git commit`の`commit`部分など)、サブコマンド(`git stash pop`の`pop`部分など)を簡単に構造体で定義でき、名前と関数を登録するだけでできる
- オプションは`Flag`として定義でき、エイリアス（`--version`を`-v`でいけるようにするやつ）もまとめて定義できる
  - `Flag`は`StringFlag`と`BoolFlag`を利用することができ、初期値の設定ができたり簡単にデータ取得ができたりする。
- コードが**読みやすい**（当社比）

#  使い方の解説

## インストール

```bash
$ go get github.com/urfave/cli
```

でパッケージをインストールしておく

## 基準となるポイントを作る

```go:main.go
package main

import (
	"os"
	"github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()
  app.Run(os.Args)
}
```

`app`の中に基本的な情報が入っており、これを編集・実行する形となる

## 名前と使い方とバージョンを登録して、実行してみる

入力された引数の文字列を表示するサンプルアプリをつくる

```go:main.go
package main

import (
	"os"
	"github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()
  
  app.Name = "sampleApp"
  app.Usage = "This app echo input arguments"
  app.Version = "0.0.1"
  
  app.Run(os.Args) // 忘れない！（よくやらかす）
}
```

保存して実行！

```bash
$ go run main.go
NAME:
   sampleApp - This app echo input arguments

USAGE:
   main [global options] command [command options] [arguments...]

VERSION:
   0.0.1

COMMANDS:
     help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --help, -h     show help
   --version, -v  print the version
```

`Action`と呼ばれる処理を登録してない場合、自動で作成されたコマンドのヘルプが表示される。

## 処理を登録する

Goの関数を登録することで処理を追加することができる

```go:main.go
package main

import (
	"os"
	"fmt"
	"github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()
  
  app.Name = "sampleApp"
  app.Usage = "This app echo input arguments"
  app.Version = "0.0.1"
  
  app.Action = func (context *cli.Context) error {
    fmt.Println(context.Args().Get(0))
    return nil
  }
  
  app.Run(os.Args)
}
```

コマンドの引数を受け取って、`fmt#Println`で表示するだけの処理を追加。

### Actionの引数

```go
func (context *cli.Context) error {}
```

としているが、`context`が何者なのかを軽く説明



`context`の中にはアプリについての情報やコマンド引数の値などが格納されている構造体である。

中でも**`Context#Args()`**と**`Context#String(), Context#Bool()`**はよく使う。



#### Context#Args()

コマンド引数のリストを取得するメソッド。

返り値は`Args`で、実態は`string`のSliceである。



ここからデータを取得するときは、`Args#Get()`を利用する。

#### Context#String(), Context#Bool()

コマンドに指定したオプションの値を取得するメソッド。

返り値はそれぞれ、 `string`と`bool`の値が返る



上記のコードではオプションを付けていなかったので、オプションの作り方を以下で紹介しておく

```go:main.go
package main

import (
	"os"
	"fmt"
	"github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()
  
  app.Name = "sampleApp"
  app.Usage = "This app echo input arguments"
  app.Version = "0.0.1"
  
  app.Action = func (context *cli.Context) error {
    if context.Bool("cat") {
    	fmt.Println(context.Args().Get(0) + "だにゃん♡")  
    } else {
	    fmt.Println(context.Args().Get(0))
    }
    return nil
  }
  
  app.Flags = []cli.Flag{
    cli.BoolFlag {
      Name: "cat, c",
      Usage: "Echo with cat",
    },
  }
  
  app.Run(os.Args)
}

```

オプションの定義は `app#Flags`に`[]cli.Flag`を登録することで定義でき、
呼び出す際は`context#{String|Bool}`にオプションの名前を指定することで値を取得できる。



定義時の`cli.BoolFlag, StringFlag`の構造体にはデフォルトの値の指定やヘルプに表示するか否かが指定できたりして、かなり柔軟に指定できる印象。



## 保存して実行！

```bash
$ go run main.go "Hello world"
Hello world
$ go run main.go -c "Hello world"
Hello worldだにゃん♡
```

無事に引数の文字列をechoできるcliアプリができている。



あとはbuild、installすればどこでも使える自分用cliツールの完成！👍

## ビルド

```bash	
$ go build
# => main.go（mainパッケージ）があるディレクトリの名前のバイナリファイルが用意される。
```

## インストール

$GOPATHを通しておけば、`go install`コマンドで$GOPATH/binディレクトリ下にバイナリファイルがインストールされて通常のコマンドのように叩くことができるようになる！



# まとめ

以上、Golangで簡単にcliツールを作ることができるパッケージについての紹介でした。

Golang使いやすいし、なにかコマンドラインツール作って見るときはGolang + urfave/cli で作ってみることをおすすめします！