はじめに
command line cliなscriptを作りたいとき、
たいていどの言語でも引数を扱うライブラリがありますよね。
golangではとても便利なurfave/cliというパッケージがあります。
(以前は github.com/codegangsta/cli
というリポジトリでした)
この使い方を簡単にメモ
- getting start的な
- Arguments とか
- Flags 使ってみる
- Subcommands でいろいろ
その前に
この記事に使用したソースです。
https://github.com/tweeeety/go-command-line-sample/tree/master/src/script
1. getting start的な
install
go get
するだけです。
glide
使ってればglide install
するだけですね。
$ go get github.com/urfave/cli
使ってみる
前提
これ以降の記述は、すべて以下のようなdir構成になっているものとして進めます。
今回の記事ようにgo-command-line-sample
というプロジェクト ディレクトリを作ってます。
# 作成したgo-command-line-sample
$ pwd
パス/go-command-line-sample
$ tree -L 3 ../go-command-line-sample
../go-command-line-sample
└── src
└── script # この配下にscriptごとにdirを切る
├── glide.lock
├── glide.yaml
└── vendor
簡単に試す
まずはprintするだけ、help出すだけ。
公式と同じですね
src/script/cli_10/main.go
package main
import (
"10t"
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Name = "boom"
app.Usage = "make an explosive entrance"
app.Action = func(c *cli.Context) error {
fmt.Println("boom! I say!")
return nil
}
app.Run(os.Args)
}
実行
# 叩いてみる
$ go run src/script/cli_10/main.go
boom! I say!
# helpってみる
$ go run src/script/cli_10/main.go help
NAME:
boom - make an explosive entrance
USAGE:
main [global options] command [command options] [arguments...]
VERSION:
0.0.0
COMMANDS:
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--help, -h show help
--version, -v print the version
go install
おもむろにgo install
してみます。
# scriptがいる場所まで移動
$ cd src/script/cli_10
# install
$ go instal
# プロジェクト直下にもどる
$ cd パス/go-command-line-sample
# binにcli_10が出来てる
$ tree -L 3
.
├── bin
│ └── cli_10
├── pkg
│ └── darwin_amd64
│ └── script
└── src
└── script
├── cli_10
├── glide.lock
├── glide.yaml
└── vendor
# たたいてみる
$ ./bin/cli_10
boom! I say!
ここまで動かすのもめっちゃ簡単ですね!
2. Arguments とか
app := cli.NewApp()
した後の基本的な使い方です。
src/script/cli_20/main.go
package main
import (
"fmt"
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Name = "cli_20"
app.Usage = "cli_20 sample"
app.Version = "1.2.1"
app.Before = func(c *cli.Context) error {
fmt.Println("-- Before --")
return nil
}
app.Action = func(c *cli.Context) error {
fmt.Println("-- Action --")
fmt.Printf("c.NArg() : %+v\n", c.NArg())
fmt.Printf("c.Args() : %+v\n", c.Args())
fmt.Printf("c.Args().Get(0) : %+v\n", c.Args().Get(0))
fmt.Printf("c.Args()[0] : %+v\n", c.Args()[0])
fmt.Printf("c.FlagNames : %+v\n", c.FlagNames())
cli.ShowVersion(c)
return nil
}
app.After = func(c *cli.Context) error {
fmt.Println("-- After --")
return nil
}
app.HideHelp = true
app.Run(os.Args)
}
実行
# そのまま実行
$ go run src/script/cli_20/main.go hoge fuga piyo
-- Before --
-- Action --
c.NArg() : 3
c.Args() : [hoge fuga piyo]
c.Args().Get(0) : hoge
c.Args()[0] : hoge
c.FlagNames : []
cli_20 version 1.2.1
-- After --
# help
$ go run main.go help
-- Before --
NAME:
cli_20 - cli_20 sample
USAGE:
main [global options] command [command options] [arguments...]
VERSION:
1.2.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
-- After --
# `app.HideHelp = true` なのでhelpは引数として見なされる
$ go run src/script/cli_20/main.go help
-- Before --
-- Action --
c.NArg() : 1
c.Args() : [help]
c.Args().Get(0) : help
c.Args()[0] : help
c.String() :
cli_20 version 1.2.1
-- After --
context
c.XXXX
で取れるcontextについてはこの辺を見るとかなりたくさんありますね。
https://godoc.org/github.com/urfave/cli#Context
3. Flags 使ってみる
flagsは-lang english
や-l english
的なオプションを受け取るためのものです。
ショートオプションや省略時のdefault optionも指定できます。
command、option、flagとは?というところは以下が大変参考になります。
Go言語によるCLIツール開発とUNIX哲学について
使ってみる
src/script/cli_30/main.go
package main
import (
"fmt"
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Name = "cli_30"
app.Usage = "cli_30 sample"
app.Version = "1.2.1"
os.Setenv("SAMPLE_ENV", "sample env dayo")
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "lang, l",
Value: "english",
Usage: "language for the greeting",
},
cli.StringFlag{
Name: "meridian, m",
Value: "AM",
Usage: "meridian for the greeting",
},
cli.StringFlag{
Name: "time, t",
Value: "07:00",
Usage: "`your time` for the greeting",
},
cli.StringFlag{
Name: "aaa, a",
Value: "sample",
EnvVar: "SAMPLE_ENV",
},
}
app.Action = func(c *cli.Context) error {
fmt.Println("-- Action --")
fmt.Printf("c.GlobalFlagNames() : %+v\n", c.GlobalFlagNames())
fmt.Printf("c.String(\"lang\") : %+v\n", c.String("lang"))
fmt.Printf("c.String(\"m\") : %+v\n", c.String("m"))
fmt.Printf("c.String(\"time\") : %+v\n", c.String("time"))
fmt.Printf("c.String(\"a\") : %+v\n", c.String("a"))
return nil
}
app.Run(os.Args)
}
実行
# 実行
# 引数のkey:valueはスペースでもイコールでもいける
$ go run src/script/cli_30/main.go -l hoge -m=PM
-- Action --
c.GlobalFlagNames() : [aaa lang meridian time]
c.String("lang") : hoge
c.String("m") : PM
c.String("time") : 07:00
c.String("a") : sample env dayo
# help
$ go run src/script/cli_30/main.go -h
NAME:
cli_30 - cli_30 sample
USAGE:
main [global options] command [command options] [arguments...]
VERSION:
1.2.1
COMMANDS:
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--aaa value, -a value (default: "sample") [$SAMPLE_ENV]
--lang value, -l value language for the greeting (default: "english")
--meridian value, -m value meridian for the greeting (default: "AM")
--time your time, -t your time your time for the greeting (default: "07:00")
--help, -h show help
--version, -v print the version
Flagの種類
Flagとして設定できる種類がいくつかあるのでその例です。
StringFlag
やBoolFlag
が指定できます。
src/script/cli_31/main.go
package main
import (
"fmt"
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Name = "cli_31"
app.Usage = "cli_31 sample"
app.Version = "1.2.1"
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "name, n",
Value: "tarou",
Usage: "your name",
},
cli.BoolFlag{
Name: "gay, g",
Usage: "are you gay boy?",
},
}
app.Action = func(c *cli.Context) error {
fmt.Println("-- Action --")
fmt.Printf("c.GlobalFlagNames() : %+v\n", c.GlobalFlagNames())
fmt.Printf("c.String(\"name\") : %+v\n", c.String("name"))
fmt.Printf("c.String(\"g\") : %+v\n", c.Bool("g"))
return nil
}
app.Run(os.Args)
}
実行
# 指定無しで叩いてみる
$ go run src/script/cli_31/main.go
-- Action --
c.GlobalFlagNames() : [name gay]
c.String("name") : tarou
c.String("g") : false
# 指定有りで叩いてみる
# BoolFlagは、オプションを指定することでtrueとなるFlag
$ go run src/script/cli_31/main.go --name hoge -g
-- Action --
c.GlobalFlagNames() : [name gay]
c.String("name") : hoge
c.String("g") : true
4. Subcommands でいろいろ
公式の通りですが、
git-like
のようなコマンドが設定できます。
普通に使ってみる
まずは公式のお試し
src/script/cli_40/main.go
package main
import (
"fmt"
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Name = "cli_40"
app.Usage = "cli_40 sample"
app.Commands = []cli.Command{
{
Name: "add",
Aliases: []string{"a"},
Usage: "add a task to the list",
Action: func(c *cli.Context) error {
fmt.Println("added task: ", c.Args().First())
return nil
},
},
{
Name: "complete",
Aliases: []string{"c"},
Usage: "complete a task on the list",
Action: func(c *cli.Context) error {
fmt.Println("completed task: ", c.Args().First())
return nil
},
},
{
Name: "template",
Aliases: []string{"t"},
Usage: "options for task templates",
Subcommands: []cli.Command{
{
Name: "add",
Usage: "add a new template",
Action: func(c *cli.Context) error {
fmt.Println("new task template: ", c.Args().First())
return nil
},
},
{
Name: "remove",
Usage: "remove an existing template",
Action: func(c *cli.Context) error {
fmt.Println("removed task template: ", c.Args().First())
return nil
},
},
},
},
}
app.Run(os.Args)
}
実行
実行してみるとこんな感じ
# addコマンドとパラメータで実行
$ go run src/script/cli_40/main.go t add hoge
added task: hoge
# t(template)コマンドとaddサブコマンドで実行
$ go run src/script/cli_40/main.go t t add hoge
new task template: hoge
$ go run src/script/cli_40/main.go -h
NAME:
cli_04 - cli_04 sample
USAGE:
main [global options] command [command options] [arguments...]
VERSION:
0.0.0
COMMANDS:
add, a add a task to the list
complete, c complete a task on the list
template, t options for task templates
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--help, -h show help
--version, -v print the version
COMMANDS
のtemplate
のSubcommandsが表示されないじゃないかーと思いますが
template
まで打って-h
すればちゃんと出ます。
かしこい!
$ go run cli_40/main.go t -h
NAME:
cli_04 template - options for task templates
USAGE:
cli_04 template command [command options] [arguments...]
COMMANDS:
add add a new template
remove remove an existing template
OPTIONS:
--help, -h show help
before/action/afterとの関係を見る
COMMANDS(のAction)とbefore/action/after
らへんはどういう関係で動くか。
src/script/cli_41/main.go
import (
"fmt"
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Name = "cli_41"
app.Usage = "cli_41 sample"
// before
app.Before = func(c *cli.Context) error {
fmt.Println("-- Before --")
return nil
}
// command action
app.Commands = []cli.Command{
// `go run cli_04/main.go sample パラメータ`でactionするコマンド
{
Name: "sample",
Aliases: []string{"s"},
Usage: "sample task",
Action: func(c *cli.Context) error {
fmt.Println("-- Sample Action --")
fmt.Println("sample task: ", c.Args().First())
return nil
},
},
}
// action
app.Action = func(c *cli.Context) error {
fmt.Println("-- Nomal Action --")
return nil
}
// after
app.After = func(c *cli.Context) error {
fmt.Println("-- After --")
return nil
}
app.Run(os.Args)
}
実行
COMMANDSが渡されたときはapp.Action
は実行されず、
COMMANDSが何も渡されないとapp.Action
が実行されます。
$ go run src/script/cli_41/main.go sample hoge
-- Before --
-- Sample Action --
sample task: hoge
-- After --
$ go run src/script/cli_41/main.go
-- Before --
-- Nomal Action --
-- After --
c.Commandを見てみる
COMMANDS
に指定したAction: func
内で使えるc.Command
を見てみます。
こんなのが使えるよー程度ですね。
src/script/cli_42/main.go
package main
import (
"fmt"
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Name = "cli_42"
app.Usage = "cli_42 sample"
app.Commands = []cli.Command{
{
Name: "sample",
Aliases: []string{"s"},
Usage: "sample task",
Action: func(c *cli.Context) error {
fmt.Println("-- Sample Action --")
fmt.Printf("c.Command.FullName() : %+v\n", c.Command.FullName())
fmt.Printf("c.Command.HasName(\"sample\") : %+v\n", c.Command.HasName("sample"))
fmt.Printf("c.Command.Names() : %+v\n", c.Command.Names())
fmt.Printf("c.Command.VisibleFlags() : %+v\n", c.Command.VisibleFlags())
return nil
},
},
}
app.Run(os.Args)
}
実行
$ go run src/script/cli_42/main.go sample hoge
-- Sample Action --
c.Command.FullName() : sample
c.Command.HasName("sample") : true
c.Command.Names() : [sample s]
c.Command.VisibleFlags() : [--help, -h show help]
Subcommands x Flags
次はSubcommands x Flags
の例です。
Flags単独の例は前述してますが、各Subcommandsごとにも設定できます。
src/script/cli_43/main.go
import (
"fmt"
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Name = "cli_43"
app.Usage = "cli_43 sample"
// command action
app.Commands = []cli.Command{
// addコマンドに対してFlagsを設定
{
Name: "add",
Aliases: []string{"a"},
Usage: "add a task to the list",
Action: func(c *cli.Context) error {
fmt.Println("added task: ", c.Args().First())
fmt.Println("json : ", c.String("j"))
fmt.Println("exec : ", c.String("e"))
return nil
},
Flags: []cli.Flag{
cli.StringFlag{
Name: "json, j",
Value: "add.json",
},
cli.BoolFlag{
Name: "exec, e",
},
},
},
// completeコマンドに対してFlagsを設定
{
Name: "complete",
Aliases: []string{"c"},
Usage: "complete a task on the list",
Action: func(c *cli.Context) error {
fmt.Println("completed task: ", c.Args().First())
fmt.Println("csv : ", c.String("c"))
fmt.Println("exec : ", c.String("e"))
return nil
},
Flags: []cli.Flag{
cli.StringFlag{
Name: "csv, c",
Value: "add.json",
},
cli.BoolFlag{
Name: "exec, e",
},
},
},
}
app.Run(os.Args)
}
実行
# addコマンドを実行。addコマンドに指定したFlagsを指定
$ go run src/script/cli_43/main.go add -j test.json -e
added task:
json : test.json
exec : true
# addコマンドを実行。completeコマンドに指定したFlagsを指定
# オプションが違うよ、という事でhelpが出る
$ go run src/script/cli_43/main.go add -c test.csv -e
Incorrect Usage: flag provided but not defined: -c
NAME:
main add - add a task to the list
USAGE:
main add [command options] [arguments...]
OPTIONS:
--json value, -j value (default: "add.json")
--exec, -e
# completeコマンドを実行。addコマンドに指定したFlagsを指定
$ go run src/script/cli_43/main.go complete -c test.csv
completed task:
csv : test.csv
exec : false
おわり
urfave/cli いろいろてんこもりで便利ですね。
ここまでがっつり機能いらないよって場合は、標準のflag パッケージの方を使ってサクっと作るのも良さそうです
\(^o^)/