现代化的命令行框架Cobra

现代化的命令行框架:Cobra 全解

Cobra 是一个可以创建强大的现代 CLI 应用程序的库,它还提供了一个可以生成应用和命令文件的程序的命令行工具:cobra-cli。有许多大型项目都是用 cobra 来构建他们的应用程序,例如:kubernetes、Docker、Etcd、Rkt、Hugo 等。Cobra 具有很多特性,一些核心特性如下:

  • 可以构建基于子命令的 CLI,并支持支持嵌套子命令。例如:app serverapp fetch

  • 可以通过 cobra-cli init appname & cobra-cli add cmdname 轻松生成应用和子命令。

  • 智能化命令建议 (app srver... did you mean app server?)。

  • 自动生成命令和标志的 help 文本,并能自动识别 -h--help 等标志。

  • 自动为你的应用程序生成 bash、zsh、fish 和 powershell 自动补全脚本。

  • 支持命令别名、自定义帮助、自定义用法等。

  • 可以与 viper、pflag 紧密集成,用于构建 12-factor 应用程序。

Cobra 建立在 commands、arguments 和 flags 结构之上。commands 代表命令,arguments 代表非选项参数,flags 代表选项参数(也叫标志)。一个好的应用程序应该是易懂的,用户可以清晰的知道如何去使用这个应用程序。应用程序通常遵循如下模式:APPNAME VERB NOUN --ADJECTIVE 或者 APPNAME COMMAND ARG --FLAG,例如:

git clone URL --bare # clone 是一个命令,URL 是一个非选项参数,bare 是一个选项参数

这里,VERB 代表动词,NOUN 代码名词,ADJECTIVE 代表形容词。

cobra-cli 命令安装

Cobra 提供了一个 cobra-cli 命令,用来初始化一个应用程序并为其添加命令,方便我们开发基于 Cobra 的应用。cobra-cli 命令安装方法如下:

$ go install github.com/spf13/cobra-cli@latest

cobra-cli 命令提供了 4 个子命令:

  • init:初始化一个 cobra 应用程序;

  • add:给通过 cobra init 创建的应用程序添加子命令;

  • completion:为指定的 shell 生成命令自动补全脚本;

  • help:打印任意命令的帮助信息。

cobra-cli 命令还提供了一些全局的参数:

  • -a, --author:指定 Copyright 版权声明中的作者;

  • --config:指定 cobra 配置文件的路径;

  • -l, --license:指定生成的应用程序所使用的开源协议,内置的有:GPLv2, GPLv3, LGPL, AGPL, MIT, 2-Clause BSD or 3-Clause BSD;

  • --viper:使用 viper 作为命令行参数解析工具,默认为 true

Cobra 使用方法

提示:请确保 Go 版本 >= 1.18

在构建 cobra 应用时,我们可以自行组织代码目录结构,但 cobra 建议如下目录结构:

main.go 文件目的只有一个:初始化 cobra 应用:

使用 cobra-cli 命令生成应用程序并添加子命令

我们可以选择使用 cobra-cli 命令行工具,来快速生成一个应用程序,并为其添加子命令,然后基于生成的代码进行二次开发,提高开发效率,具体步骤如下:

  1. 生成应用程序

可以使用 cobra-cli init 命令初始化一个应用程序,然后我们就可以基于这个 Demo 程序做二次开发,提高开发效率。如下命令可以初始化一个新的应用程序:

提示:如果遇到错误 Error: invalid character '{' after top-level value)'}',可参考:https://github.com/spf13/cobra-cli/issues/26。

当一个应用程序被初始化之后,就可以给这个应用程序添加一些命令:

执行 cobra-cli add 之后,会在 cmd 目录下生成命令源码文件。cobra-cli add 不仅可以添加命令,也可以添加子命令,例如上面的例子,通过 cobra-cli add create -p 'configCmd'config 命令添加了 create 子命令,-p 指定子命令的父命令:<父命令>Cmd

  1. 编译并执行

在生成完命令后,可以直接执行 go build 命令编译应用程序:

这里需要注意:命令名称要是 camelCase 格式,而不是 snake_case / snake-case 格式,如果不是驼峰格式,cobra 会报错。

  1. 配置 cobra

cobra 在生成应用程序时,也会在当前目录下生成 LICENSE 文件,并且会在生成的 Go 源码文件中,添加 LICENSE Header,LICENSE 和 LICENSE Header 的内容可以通过 cobra 配置文件进行配置,默认的配置文件为:~/.cobra.yaml,例如:

在如上例子中,{{ .copyright }} 的具体内容会根据 authoryear 生成,根据此配置生成的 LICENSE 文件内容为:

我们也可以使用内建的 licenses,内建的 licenses 有:GPLv2, GPLv3, LGPL, AGPL, MIT, 2-Clause BSD or 3-Clause BSD。例如,我们使用 MIT license:

使用 cobra 库创建命令

如果要用 cobra 库编码实现一个应用程序,需要首选创建一个空的 main.go 文件和一个 rootCmd 文件,之后可以根据需要添加其它命令。具体步骤如下;

  1. 创建 rootCmd

通常情况下,我们会将 rootCmd 放在文件 cmd/root.go 中:

还可以在 init() 函数中定义标志和处理配置,例如:cmd/helper.go

  1. 创建 main.go

我们还需要一个 main 函数来 调用 rootCmd,通常我们会创建一个 main.go 文件,在 main.go 中调用 rootCmd.Execute() 来执行命令:

需要注意,main.go 中不建议放很多代码,通常只需要调用 cmd.Execute() 即可。

  1. 添加命令

除了 rootCmd,我们还可以调用 AddCommand 添加其它命令,通常情况下,我们会把其它命令的源码文件放在 cmd/ 目录下,例如,我们添加一个 version 命令,可以创建 cmd/version.go 文件,内容为:

本示例中,我们通过调用 rootCmd.AddCommand(versionCmd)rootCmd 命令添加了一个 versionCmd 命令。

  1. 编译并运行

替换 main.go{pathToYourApp} 为对应的路径,例如本示例中 pathToYourAppgithub.com/nosbelm/miniblogdemo/cobrademolib

通过步骤 1、2、3 我们就成功创建和添加了 cobra 应用程序和其命令。

使用标志

cobra 可以跟 pflag 结合使用,实现强大的标志功能。使用步骤如下:

  1. 使用持久化的标志

标志可以是“持久的”,这意味着该标志可用于它所分配的命令以及该命令下的每个子命令。可以在 rootCmd 上定义持久标志:

  1. 使用本地标志

也可以分配一个本地标志,本地标志只能在其所绑定的命令上使用:

--source 标志只能在 rootCmd 上引用,而不能在 rootCmd 的子命令上引用。

  1. 将标志绑定到 viper

我们可以将标志绑定到 viper,这样就可以使用 viper.Get() 获取标志的值。

  1. 设置标志为必选

默认情况下,标志是可选的,我们也可以设置标志位必选,当设置标志位必选,但是没有提供标志时,cobra 会报错。

  1. 非选项参数验证

在使用命令的过程中,经常会传入非选项参数,并且需要对这些非选项参数进行验证,cobra 提供了机制来对非选项参数进行验证。可以使用 CommandArgs 字段来验证非选项参数。cobra 也内置了一些验证函数,具体见下表:

函数
描述

NoArgs

如果存在任何非选项参数,该命令将报错

ArbitraryArgs

该命令将接受任何非选项参数

OnlyValidArgs

如果有任何非选项参数不在 CommandValidArgs 字段中,该命令将报错

MinimumNArgs(int)

如果没有至少 N 个非选项参数,该命令将报错

MaximumNArgs(int)

如果有多于 N 个非选项参数,该命令将报错

ExactArgs(int)

如果非选项参数个数不为 N,该命令将报错

ExactValidArgs(int)

如果非选项参数的个数不为 N,或者非选项参数不在 CommandValidArgs 字段中,该命令将报错

RangeArgs(min, max)

如果非选项参数的个数不在 minmax 之间,该命令将报错

使用自定义验证函数,示例如下:

也可以自定义验证函数,示例如下:

Help命令

在使用应用程序时,我们需要知道该应用程序的调用方法,所以需要有一个 Help 命令或者选项参数,Cobra 的强大之处也在于所有我们需要的功能 cobra 都已经帮我们实现好了。在用 cobra 构建应用程序时,cobra 会自动为应用程序添加一个帮助命令,当用户运行 app help 时会调用此方法。此外,当 help 的输入为其它命令时,会打印该命令的用法,比如有一个叫做 create 的命令,当调用 app help create 时,会打印 create 的帮助信息。cobra 也会给每个命令自动添加 --help 标志。例如:

我们也可以定义自己的 help 命令。使用如下函数,可以定义 help 命令:

使用信息

当用户提供无效标志或无效命令时,cobra 会打印出 usage 信息。例如:

help 一样,我们也可以自定义 usage,通过如下看函数可以自定义 usage:

version 标志

如果在 rootCmd 命令上设置了 Version 字段,Cobra 会添加持久的 --version 标志。运行应用程序时,指定了 --version 标志,应用程序会使用 Version 模板将版本打印到 stdout。可以使用 cmd.SetVersionTemplate(s string) 函数自定义 Version 模板。

PreRun and PostRun Hooks

在运行 Run 函数时我们可以运行一些钩子函数,比如 PersistentPreRunPreRun 函数在 Run 函数之前执行。PersistentPostRunPostRunRun 函数之后执行。如果子命令没有指定 Persistent*Run 函数,则子命令将会继承父命令的 Persistent*Run 函数。这些函数的运行顺序如下:

  1. PersistentPreRun

  2. PreRun

  3. Run

  4. PostRun

  5. PersistentPostRun

注意父级的 PreRun 只会在父级命令运行时调用,子命令时不会调用的。

下面是使用所有这些函数的两个命令的示例。执行子命令时,它将运行 rootCmd 命令的 PersistentPreRun,但不运行 rootCmd 命令的 PersistentPostRun

执行后,输出如下:

命令建议

Cobra 还有很多其它有用的特性,比如当我们输入的命令有误时,cobra 会根据注册的命令,推算出可能的命令。例如,当 unknown command 错误发生时,Cobra 将自动打印建议的命令:

根据注册的每个子命令自动建议并使用 Levenshtein distance 实现。每个匹配最小距离为 2(忽略大小写)的注册命令将显示为建议。如果需要在命令中禁用建议或调整字符串距离,可以使用:

或者:

需要注意,Levenshtein distance(编辑距离)是针对二个字符串(例如英文字)的差异程度的量化量测,量测方式是看至少需要多少次的处理才能将一个字符串变成另一个字符串。

还可以使用 SuggestFor 属性显式设置要为其指定命令的名称,例如:

执行 newApp cfg

Last updated

Was this helpful?