无论是Openstack还是Docker都有一个漂亮的命令行工具,Openstack的命令行工具主要使用的是Python的argparse库,至于Docker的CLI的实现还没看,但是今天看到了一个在Golang中 用于构建像Docker命令行风格的一个库:cobra

cobra简介

Cobra既是一个用来创建强大的现代CLI命令行的golang库,也是一个生成程序应用和命令行文件的程序。
它提供的功能有:

  • 简易的子命令行模式,如 app server, app fetch等等
  • 完全兼容posix命令行模式
  • 嵌套子命令subcommand
  • 支持全局,局部,串联flags
  • 使用Cobra很容易的生成应用程序和命令,使用cobra create appname和cobra add cmdname
  • 如果命令输入错误,将提供智能建议,如 app srver,将提示srver没有,是否是app server
  • 自动生成commands和flags的帮助信息
  • 自动生成详细的help信息,如app help
  • 自动识别-h,–help帮助flag
  • 自动生成应用程序在bash下命令自动完成功能
  • 自动生成应用程序的man手册
  • 命令行别名
  • 自定义help和usage信息
  • 可选的紧密集成的viper apps

从功能上看完全超越了argparse, 下面将做一个简单的测试,体验下cobra的强大

安装

安装cobra需要翻墙,我的环境是Mac,使用ss + polipo来提供https的方向代理。我的代理端口在8123,所以命令行是这样的

$https_proxy=localhost:8123 go get -v github.com/spf13/cobra/cobra

安装完成后可以看到cobra的一些帮助信息

maojun@maojun-mbp$ cobra -h
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.
Usage:
  cobra [command]
Available Commands:
  add         Add a command to a Cobra Application
  init        Initialize a Cobra Application
Flags:
  -a, --author string        Author name for copyright attribution (default "YOUR NAME")
      --config string        config file (default is $HOME/.cobra.yaml)
  -l, --license license      Name of license for the project (can provide license in config)
  -b, --projectbase string   base project directory, e.g. github.com/spf13/
      --viper                Use Viper for configuration (default true)
Use "cobra [command] --help" for more information about a command.

使用

接下来将使用cobra构建一个不带子命令的CLI和带子命令的CLI

初始化

我们可以通过cobra提供的init命令来生成CLI的框架代码,因此切换到GOPATH/src下面,初始CLI框架

maojun@maojun-mbp$ cobra init demo
Your Cobra application is ready at
/Users/maojun/GoWorkDir/src/demo
Give it a try by going there and running `go run main.go`
Add commands to it by running `cobra add [cmdname]`

这个命令会帮你生成这样一个框架代码

maojun@maojun-mbp$ tree demo
demo
├── LICENSE
├── cmd
│   └── root.go
└── main.go

简单的CLI

在写一些简单的CLI的时候我们其实是不需要有子命令的,我们往往需要这样一种简单的CLI

demo.exe
Demo is a test appcation for print things
Usage:
  demo [flags]
Flags:
  -a, --age int       person's age
  -h, --help          help for demo
  -n, --name string   person's name

接下来我们就在上面生成的代码的基础上完成一个不带子命令的CLI。首先,我需要编写我的业务逻辑,因此我在demo下面新建一个包,名称为simple。如下:

maojun@maojun-mbp$ tree .
.
├── LICENSE
├── cmd
│   └── root.go
├── main.go
└── simple
    ├── simple.go
    └── simple_test.go

这里仅仅实现一个print作为样例,因此simple.go是这样实现的

package simple
import (
    "fmt"
)
func Show(name string, age int) {
    fmt.Printf("My name is %s, my age is %d\n", name, age)
}

接下来我们需要将我们实行的整个Show方法暴露给CLI, 我们从生成的main文件入手分析。

  1. 在main里面调用了 demo/cmd包里面暴露的Execute 函数 [cmd.Execute()]
  2. 在demo/cmd/root.go中发现Execute执行的是RootCmd.Execute()
  3. 而RootCmd是一个cobra的Command结构体[RootCmd = &cobra.Command]
    显然我们想要实行不带子命令的CLI,只需要将RootCmd的修改成我们需要的结构体就ok了

这里做了几点修改

  1. RootCmd中的Command结构体中的Run方法需要我们定义, 主要功能就是调用simple里面的Show接口
  2. cmd包初始化得时候需要通过RootCmd.Flags()获取命令行传入的name和age的参数,因此这里需要修改init方法
  3. 最后我们不需要从配置文件读取配置,注释掉:nitConfig函数和”http://github.com/spf13/viper”

最终这个root.go是这样的

最后测试下是不是我们想要的效果

maojun@maojun-mbp$ go run main.go -h
Demo is a test appcation for print things
Usage:
  demo [flags]
Flags:
  -a, --age int       person's age
  -n, --name string   persion's name
maojun@maojun-mbp$ go run main.go -n "test" -a 10
My name is test, my age is 10

带子命令的CLI

对于复杂的情况,往往需要带子命令场景,比如Docker的CLI,而最终的效果应该是这样的

demo
Demo is a test appcation for print things
Usage:
  demo [flags]
  demo [command]
Available Commands:
  test        A brief description of your command
Flags:
  -a, --age int       person's age
  -h, --help          help for demo
  -n, --name string   person's name
Use "demo [command] --help" for more information about a command.

支持子命令是cobra的自己的功能,因此直接可以通过cobra生成带子命令的代码

maojun@maojun-mbp$ cobra init demo
Your Cobra application is ready at
/Users/maojun/GoWorkDir/src/demo
Give it a try by going there and running `go run main.go`
Add commands to it by running `cobra add [cmdname]`
maojun@maojun-mbp$ cobra add test
test created at /Users/maojun/GoWorkDir/src/cmd/test.go

注释掉root.go那些不需要的地方, 然后修改生成的test.go

最后测试下是不是我们想要的效果

maojun@maojun-mbp$ go run main.go -h
A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:

Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.

Usage:
  demo [command]

Available Commands:
  test        A brief description of your command

Flags:
      --config string   config file (default is $HOME/.demo.yaml)
  -t, --toggle          Help message for toggle

Use "demo [command] --help" for more information about a command.

maojun@maojun-mbp$ go run main.go test -h
A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:

Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.

Usage:
  demo test [flags]

Flags:
  -a, --age int       person's age
  -n, --name string   persion's name

Global Flags:
      --config string   config file (default is $HOME/.demo.yaml)

 maojun@maojun-mbp$ go run main.go test -a 10 -n test
My name is test, my age is 10

命令行补全,man这些可以自己手动测试。