spf13/cobraurfave/cli 是Go的2个最流行的优秀的命令行工具

对比 spf13/cobra urfave/cli
简介 A Framework for Modern CLI Apps in Go urfave/cli is a simple, fast, and fun package for building command line apps in Go.
应用项目 docker, kubernetes, istio, etcd, hugo … drone, peach, gogs, gitea …
LICENSE Apache 2.0 license MIT
flag 基于pflag, 支持更多类型参数 基于标准库flag
绑定配置参数 可以通过viper 通过altsrc包实现

spf13/cobra

下载安装

1
$ go get -u github.com/spf13/cobra/cobra

在项目中使用引入

1
import "github.com/spf13/cobra"

Cobra 建立在commands, arguments & flags的结构之上。 commands代表动作,Args是事物,Flags是这些动作的修饰符。

cobra遵循的模式是 APPNAME VERB NOUN --ADJECTIVE. or APPNAME COMMAND ARG --FLAG

Create rootCmd

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
var rootCmd = &cobra.Command{
  Use:   "hugo",
  Short: "Hugo is a very fast static site generator",
  Long: `A Fast and Flexible Static Site Generator built with
                love by spf13 and friends in Go.
                Complete documentation is available at http://hugo.spf13.com`,
  Run: func(cmd *cobra.Command, args []string) {
    // Do Stuff Here
  },
}

func Execute() {
  if err := rootCmd.Execute(); err != nil {
    fmt.Println(err)
    os.Exit(1)
  }
}

添加子命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package cmd

import (
  "fmt"

  "github.com/spf13/cobra"
)

func init() {
  rootCmd.AddCommand(versionCmd)
}

var versionCmd = &cobra.Command{
  Use:   "version",
  Short: "Print the version number of Hugo",
  Long:  `All software has versions. This is Hugo's`,
  Run: func(cmd *cobra.Command, args []string) {
    fmt.Println("Hugo Static Site Generator v0.9 -- HEAD")
  },
}

添加命令就是在init函数中执行rootCmd.AddCommand.

cobra定义flag参数的方法

cobra用rootCmd.AddCommand添加子命令,在对应代码文件的init函数中定义flag.

支持全局参数和本地参数:

  • newCmd.PersistentFlags。全局参数,根命令和子命令下都包含了这个参数项」。可以说定义一次,全局可用。
  • newCmd.Flags。本地参数,只对当前命令下生效,其他子命令下不会继承。

提前定义存储变量:

1
2
var Verbose bool
var Source string

全局参数

1
rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")

本地参数

1
rootCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")

父命令上的本地参数

默认情况下,Cobra 仅解析目标命令上的本地标志,父命令上的任何本地标志都将被忽略。通过启用 Command.TraverseChildren Cobra 将在执行目标命令之前解析每个命令上的本地标志。

1
2
3
4
command := cobra.Command{
  Use: "print [OPTIONS] [COMMANDS]",
  TraverseChildren: true,
}

绑定配置参数

使用viper绑定配置文件中的参数

1
2
3
4
5
6
var author string

func init() {
  rootCmd.PersistentFlags().StringVar(&author, "author", "YOUR NAME", "Author name for copyright attribution")
  viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author"))
}

必须flags

默认情况下,flag是可选的。相反,如果您希望您的命令在未设置flag时报告错误,可以通过rootCmd.MarkFlagRequired将其标记为必需:

1
2
rootCmd.Flags().StringVarP(&Region, "region", "r", "", "AWS region (required)")
rootCmd.MarkFlagRequired("region")

参数位置和自定义

  • NoArgs: 如果包含任何位置参数,命令报错
  • ArbitraryArgs: 命令接受任何参数
  • OnlyValidArgs: 如果有位置参数不在ValidArgs中,命令报错
  • MinimumArgs(init): 如果参数数目少于N个后,命令行报错
  • MaximumArgs(init): 如果参数数目多余N个后,命令行报错
  • ExactArgs(init): 如果参数数目不是N个话,命令行报错
  • RangeArgs(min, max): 如果参数数目不在范围(min, max)中,命令行报错

Suggestions when “unknown command” happens

当发生 “unknown command” 错误时,会自动打印建议帮助.

1
2
3
4
5
6
7
$ hugo srever
Error: unknown command "srever" for "hugo"

Did you mean this?
        server

Run 'hugo --help' for usage.

urfave/cli

下载安装

1
$ go get github.com/urfave/cli/v2

在项目中使用引入

1
import "github.com/urfave/cli/v2" // imports as package "cli"

定义和查询flag

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package main

import (
    "fmt"
    "log"
    "os"

    "github.com/urfave/cli/v2"
)

func main() {
    app := &cli.App{
        Flags: []cli.Flag{
            &cli.StringFlag{
                Name:  "lang",
                Value: "english",
                Usage: "language for the greeting",
            },
        },
        Action: func(cCtx *cli.Context) error {
            name := "Nefertiti"
            if cCtx.NArg() > 0 {
                name = cCtx.Args().Get(0)
            }
            if cCtx.String("lang") == "spanish" {
                fmt.Println("Hola", name)
            } else {
                fmt.Println("Hello", name)
            }
            return nil
        },
    }

    if err := app.Run(os.Args); err != nil {
        log.Fatal(err)
    }
}

设置别名和默认值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import (
    "log"
    "os"

    "github.com/urfave/cli/v2"
)

func main() {
    app := &cli.App{
        Flags: []cli.Flag{
            &cli.StringFlag{
                Name:    "lang",
                Aliases: []string{"l"},
                Value:   "english",
                Usage:   "language for the greeting",
            },
        },
    }

    if err := app.Run(os.Args); err != nil {
        log.Fatal(err)
    }
}

可以使用 --lang spanish or -l spanish

设置环境变量

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import (
    "log"
    "os"

    "github.com/urfave/cli/v2"
)

func main() {
    app := &cli.App{
        Flags: []cli.Flag{
            &cli.StringFlag{
                Name:    "lang",
                Aliases: []string{"l"},
                Value:   "english",
                Usage:   "language for the greeting",
                EnvVars: []string{"LEGACY_COMPAT_LANG", "APP_LANG", "LANG"},
            },
        },
    }

    if err := app.Run(os.Args); err != nil {
        log.Fatal(err)
    }
}

可以通过EnvVars设置多个环境变量值

从文件读取默认值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import (
    "log"
    "os"

    "github.com/urfave/cli/v2"
)

func main() {
    app := &cli.App{
        Flags: []cli.Flag{
            &cli.StringFlag{
                Name:     "password",
                Aliases:  []string{"p"},
                Usage:    "password for the mysql database",
                FilePath: "/etc/mysql/password",
            },
        },
    }

    if err := app.Run(os.Args); err != nil {
        log.Fatal(err)
    }
}

参考