COBRA既是一个用来创建强大的现代CLI命令行的GOLANG库,也是一个生成程序应用和命令行文件的程序

git repo
- https://github.com/spf13/cobra
 
示例

Cobra提供的功能
- 简易的子命令行模式,如 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
 
如何使用
安装cobra
go get -v github.com/spf13/cobra/cobra
在安装的过程中,由于GWF(长城防火墙)的原因,golang的资源无法访问
- 解决的思路:git clone 源代码,在GOPATH的src目录中建立相关目录,将clone的源码拷贝进去
 - 注意:目录的名称会有变化
 - 举例如下:
- 直接找到github上的源码地址,Git clone https://github.com/go-yaml/yaml.git
 - 在src 目录下 创建 gopkg.in 目录 把yaml 重命名为yaml.v2
 
 
little example
package main
import (
    "fmt"
    "github.com/spf13/cobra"
)
func main() {
    
    var name string
    var food string
    var myFood string
    // 1. 主命令
    var rootCmd = &cobra.Command{
        Use:   "personLikeEat",
        Short: "Input the chinese name and food name",
        //命令执行的函数
        Run: func(cmd *cobra.Command, args []string) {
            if len(name) == 0 {
                cmd.Help()
                return
            }
            if len(food) == 0 {
                cmd.Help()
                return
            }
            fmt.Printf("%s like eat %s.\n", name, food)
        },
    }
    
    // 2. 子命令
    var subCmd = &cobra.Command{
        Use:   "ILikeEat",
        Short: "Input food information",
        //命令执行的函数
        Run: func(cmd *cobra.Command, args []string) {
            if len(myFood) == 0 {
                cmd.Help()
                return
            }
            fmt.Printf("I like eat %s.\n", myFood)
        },
    }
    
    // 添加子命令
    rootCmd.AddCommand(subCmd)
    
    // 3.1 主命令添加选项
    rootCmd.Flags().StringVarP(&name, "name", "n", "", "person's name")
    rootCmd.Flags().StringVarP(&food, "food", "f", "", "food's name")
    
    // 3.2 子命令添加选项
    subCmd.Flags().StringVarP(&myFood, "food", "f", "", "food's name")
    
    // 执行命令
    rootCmd.Execute()
}
运行结果如下:
E:\mygo\src\test\demo\personLikeEat>personLikeEat
Input the chinese name and food name
Usage:
  personLikeEat [flags]
  personLikeEat [command]
Available Commands:
  ILikeEat    Input food information
Flags:
  -f, --food string   food's name
  -n, --name string   person's name
Use "personLikeEat [command] --help" for more information about a command.
E:\mygo\src\test\demo\personLikeEat>personLikeEat -n yp -f apple
yp like eat apple.
E:\mygo\src\test\demo\personLikeEat>personLikeEat ILikeEat
Input food information
Usage:
  personLikeEat ILikeEat [flags]
Flags:
  -f, --food string   food's name
E:\mygo\src\test\demo\personLikeEat>personLikeEat ILikeEat -f banana
I like eat banana.
基本用法就是这四步:
- 主命令
 - 子命令
 - 添加选项
 - 执行命令
 
使用cobra生成应用程序
假设现在我们要开发一个基于CLIs的命令程序,名字为cobraDemo。首先打开CMD,切换到GOPATH的src目录下,执行如下指令:
E:\mygo\src>..\bin\cobra.exe init cobraDemo
Your Cobra application is ready at
E:\mygo\src\cobraDemo
Give it a try by going there and running `go run main.go`
Add commands to it by running `cobra add [cmdname]`
在src目录下会生成一个cobraDemo的文件夹,如下:
cobraDemo
│  LICENSE
│  main.go
│  
└─cmd
        root.go
如果你的cobraDemo程序没有subcommands,那么cobra生成应用程序的操作就结束了。
如何实现没有子命令的CLIs程序
接下来就是可以继续cobraDemo的功能设计了。例如我在demo下面新建一个包,名称为imp。如下:
cobraDemo
│  LICENSE
│  main.go
│  
├─cmd
│      root.go
│      
└─imp
        imp.go
imp.go文件的代码如下:
package imp
import(
    "fmt"
)
func Show(name string, age int) {
    fmt.Printf("My Name is %s, My age is %d\n", name, age)
}
cobraDemo程序成命令行接收两个参数name和age,然后打印出来。打开cobra自动生成的main.go文件查看:
// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import "cobraDemo/cmd"
func main() {
    cmd.Execute()
}
可以看出main函数执行cmd包,所以我们只需要在cmd包内调用imp包就能实现demo程序的需求。接着打开root.go文件查看:
// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
    "fmt"
    "os"
    "github.com/spf13/cobra"
    "github.com/spf13/viper"
)
var cfgFile string
// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
    Use:   "cobraDemo",
    Short: "A brief description of your application",
    Long: `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.`,
// Uncomment the following line if your bare application
// has an action associated with it:
//  Run: func(cmd *cobra.Command, args []string) { },
}
// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
    if err := RootCmd.Execute(); err != nil {
        fmt.Println(err)
        os.Exit(-1)
    }
}
func init() {
    cobra.OnInitialize(initConfig)
    // Here you will define your flags and configuration settings.
    // Cobra supports Persistent Flags, which, if defined here,
    // will be global for your application.
    RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobraDemo.yaml)")
    // Cobra also supports local flags, which will only run
    // when this action is called directly.
    RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
// initConfig reads in config file and ENV variables if set.
func initConfig() {
    if cfgFile != "" { // enable ability to specify config file via flag
        viper.SetConfigFile(cfgFile)
    }
    viper.SetConfigName(".cobraDemo") // name of config file (without extension)
    viper.AddConfigPath("$HOME")  // adding home directory as first search path
    viper.AutomaticEnv()          // read in environment variables that match
    // If a config file is found, read it in.
    if err := viper.ReadInConfig(); err == nil {
        fmt.Println("Using config file:", viper.ConfigFileUsed())
    }
}
从源代码来看cmd包进行了一些初始化操作并提供了Execute接口。十分简单,其中viper是cobra集成的配置文件读取的库,这里不需要使用,我们可以注释掉(不注释可能生成的应用程序很大约10M,这里没用到最好是注释掉)。cobra的所有命令都是通过cobra.Command这个结构体实现的。为了实现demo功能,显然我们需要修改RootCmd。修改后的代码如下:
// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
    "fmt"
    "os"
    "github.com/spf13/cobra"
    // "github.com/spf13/viper"
    "cobraDemo/imp"
)
// var cfgFile string
var name string
var age int
// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
    Use:   "cobraDemo",
    Short: "A test demo",
    Long: `Demo is a test appcation for print things`,
    // Uncomment the following line if your bare application
    // has an action associated with it:
    Run: func(cmd *cobra.Command, args []string) {
        if len(name) == 0 {
            cmd.Help()
            return
        }
        imp.Show(name, age)
    },
}
// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
    if err := RootCmd.Execute(); err != nil {
        fmt.Println(err)
        os.Exit(-1)
    }
}
func init() {
    // cobra.OnInitialize(initConfig)
    // Here you will define your flags and configuration settings.
    // Cobra supports Persistent Flags, which, if defined here,
    // will be global for your application.
    // RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobraDemo.yaml)")
    // Cobra also supports local flags, which will only run
    // when this action is called directly.
    // RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
    RootCmd.Flags().StringVarP(&name, "name", "n", "", "person's name")
    RootCmd.Flags().IntVarP(&age, "age", "a", 0, "person's age")    
}
// initConfig reads in config file and ENV variables if set.
// func initConfig() {
//  if cfgFile != "" { // enable ability to specify config file via flag
//      viper.SetConfigFile(cfgFile)
//  }
//  viper.SetConfigName(".cobraDemo") // name of config file (without extension)
//  viper.AddConfigPath("$HOME")  // adding home directory as first search path
//  viper.AutomaticEnv()          // read in environment variables that match
//  // If a config file is found, read it in.
//  if err := viper.ReadInConfig(); err == nil {
//      fmt.Println("Using config file:", viper.ConfigFileUsed())
//  }
//}
到此demo的功能已经实现了,我们编译运行一下看看实际效果:
E:\mygo\src\cobraDemo>go build
E:\mygo\src\cobraDemo>cobraDemo.exe
Demo is a test appcation for print things
Usage:
  cobraDemo [flags]
Flags:
  -a, --age int       person's age
  -n, --name string   person's name
E:\mygo\src\cobraDemo>cobraDemo.exe -n yp -a 26
My Name is yp, My age is 26
如何实现带有子命令的CLIs程序
在执行cobra.exe init demo之后,继续使用cobra为demo添加子命令test:
E:\mygo\src\cobraDemo>..\..\bin\cobra add test
test created at E:\mygo\src\cobraDemo\cmd\test.go
在src目录下demo的文件夹下生成了一个cmd\test.go文件,如下:
cobraDemo
│  cobraDemo.exe
│  LICENSE
│  main.go
│  
├─cmd
│      root.go
│      test.go
│      
└─imp
        imp.go
接下来的操作就和上面修改root.go文件一样去配置test子命令。效果如下:
E:\mygo\src\cobraDemo>go build
E:\mygo\src\cobraDemo>cobraDemo.exe
Demo is a test appcation for print things
Usage:
  cobraDemo [flags]
  cobraDemo [command]
Available Commands:
  test        A brief description of your command
Flags:
  -a, --age int       person's age
  -n, --name string   person's name
Use "cobraDemo [command] --help" for more information about a command.        
可以看出demo既支持直接使用标记flag,又能使用子命令
E:\mygo\src\cobraDemo>cobraDemo.exe 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:
  cobraDemo test [flags]
调用test命令输出信息,这里没有对默认信息进行修改。
E:\mygo\src\cobraDemo>cobraDemo.exe tet
Error: unknown command "tet" for "cobraDemo"
Did you mean this?
        test
Run 'cobraDemo --help' for usage.
unknown command "tet" for "cobraDemo"
Did you mean this?
        test
这是错误命令提示功能
函数解析
函数说明
关于代码执行顺序
// The *Run functions are executed in the following order:
//   * PersistentPreRun()
//   * PreRun()
//   * Run()
//   * PostRun()
//   * PersistentPostRun()
修改root.go文件如下:
// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
    "fmt"
    "os"
    "github.com/spf13/cobra"
    // "github.com/spf13/viper"
    "cobraDemo/imp"
)
// var cfgFile string
var name string
var age int
// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
    Use:   "cobraDemo",
    Short: "A test demo",
    Long: `Demo is a test appcation for print things`,
    // Uncomment the following line if your bare application
    // has an action associated with it:
    
    // The *Run functions are executed in the following order:
    //   * PersistentPreRun()
    //   * PreRun()
    //   * Run()
    //   * PostRun()
    //   * PersistentPostRun()  
    PersistentPreRun: func(cmd *cobra.Command, args []string) {
        fmt.Printf("Inside rootCmd PersistentPreRun with args: %v\n", args)
    },
    PreRun: func(cmd *cobra.Command, args []string) {
        fmt.Printf("Inside rootCmd PreRun with args: %v\n", args)
    },      
    Run: func(cmd *cobra.Command, args []string) {
        if len(name) == 0 {
            cmd.Help()
            return
        }
        imp.Show(name, age)
    },  
    PostRun: func(cmd *cobra.Command, args []string) {
        fmt.Printf("Inside rootCmd PostRun with args: %v\n", args)
    },
    PersistentPostRun: func(cmd *cobra.Command, args []string) {
        fmt.Printf("Inside rootCmd PersistentPostRun with args: %v\n", args)
    },  
}
// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
    if err := RootCmd.Execute(); err != nil {
        fmt.Println(err)
        os.Exit(-1)
    }
}
func init() {
    // cobra.OnInitialize(initConfig)
    // Here you will define your flags and configuration settings.
    // Cobra supports Persistent Flags, which, if defined here,
    // will be global for your application.
    // RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobraDemo.yaml)")
    // Cobra also supports local flags, which will only run
    // when this action is called directly.
    // RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
    RootCmd.Flags().StringVarP(&name, "name", "n", "", "person's name")
    RootCmd.Flags().IntVarP(&age, "age", "a", 0, "person's age")    
}
测试结果如下:
E:\mygo\src\cobraDemo>go build
E:\mygo\src\cobraDemo>cobraDemo.exe
Inside rootCmd PersistentPreRun with args: []
Inside rootCmd PreRun with args: []
Demo is a test appcation for print things
Usage:
  cobraDemo [flags]
  cobraDemo [command]
Available Commands:
  test        A brief description of your command
Flags:
  -a, --age int       person's age
  -n, --name string   person's name
Use "cobraDemo [command] --help" for more information about a command.
Inside rootCmd PostRun with args: []
Inside rootCmd PersistentPostRun with args: []
E:\mygo\src\cobraDemo>cobraDemo.exe -n yp -a 26
Inside rootCmd PersistentPreRun with args: []
Inside rootCmd PreRun with args: []
My Name is yp, My age is 26
Inside rootCmd PostRun with args: []
Inside rootCmd PersistentPostRun with args: []
E:\mygo\src\cobraDemo>cobraDemo.exe test
Inside rootCmd PersistentPreRun with args: []
test called
Inside rootCmd PersistentPostRun with args: []
当使用–help命令的时候,PersistentPreRun、PreRun、PostRun、PersistentPostRun不会被调用 示例如下:
E:\mygo\src\cobraDemo>cobraDemo.exe --help
Demo is a test appcation for print things
Usage:
  cobraDemo [flags]
  cobraDemo [command]
Available Commands:
  test        A brief description of your command
Flags:
  -a, --age int       person's age
  -n, --name string   person's name
Use "cobraDemo [command] --help" for more information about a command.
“数据类型” + “P”
此种类型支持短标记
// BoolP is like Bool, but accepts a shorthand letter that can be used after a single dash.
func (f *FlagSet) BoolP(name, shorthand string, value bool, usage string) *bool {
    p := new(bool)
    f.BoolVarP(p, name, shorthand, value, usage)
    return p
}
“数据类型” + “Var”
此种类型表示变量
// BoolVar defines a bool flag with specified name, default value, and usage string.
// The argument p points to a bool variable in which to store the value of the flag.
func BoolVar(p *bool, name string, value bool, usage string) {
    BoolVarP(p, name, "", value, usage)
}
全局短标记过时
    RootCmd.PersistentFlags().BoolP("help", "h", false, "Print usage")
    RootCmd.PersistentFlags().MarkShorthandDeprecated("help", "please use --help")
示例如下:
E:\mygo\src\cobraDemo>cobraDemo.exe -h
Flag shorthand -h has been deprecated, please use --help
Demo is a test appcation for print things
Usage:
  cobraDemo [flags]
  cobraDemo [command]
Available Commands:
  test        A brief description of your command
Flags:
  -a, --age int       person's age
      --help          Print usage
  -n, --name string   person's name
Use "cobraDemo [command] --help" for more information about a command.
E:\mygo\src\cobraDemo>cobraDemo.exe test -h
Flag shorthand -h has been deprecated, please use --help
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:
  cobraDemo test [flags]
Global Flags:
      --help   Print usage
文档信息
- 本文作者:Yu Peng
 - 本文链接:https://www.y2p.cc/2017/02/17/cobra/
 - 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)