Cobra 入门
在本课程中,你将学习 Cobra,它是 Go 命令行程序的事实标准。
Cobra 既是处理命令行参数和组织命令的库,也是一个可以为你的程序生成结构化骨架的应用程序。它被众多知名的 Go 命令行工具广泛使用,例如 kubectl(Kubernetes CLI)和 docker(Docker CLI)。
Cobra 指挥官
Cobra 的名字来源于漫画反派 Cobra Commander,他是 G.I Joe 漫画宇宙中“Cobra”恐怖组织的最高领导人。这为一个命令行程序工具提供了绝佳的灵感。它还有一个很棒的标志!

Cobra 的作者 Steve Francia 在 Google 工作,开发了多个其他著名的 Go 库和程序,特别是 Hugo 和 Viper。剧透预警:我们稍后将在 multi-git 中集成 Viper。
好的。让我们来看看 Cobra 到底是什么。
为什么选择 Cobra?
Cobra 成熟、经过实战检验,并提供了大量功能。它很可能已被其他 Go 开发者所熟悉,因此如果你与他人协作,他们使用 Cobra 构建的命令行程序会感觉非常顺手。以下是 Cobra 提供的一些特性:
- 命令与嵌套子命令
- 符合 POSIX 标准的标志(包括短名和长名)
- 全局、局部和级联标志
- 轻松生成应用程序骨架
- 智能拼写建议
- 可选地与 viper 紧密集成以支持 12 因素应用
还有更多功能。你可能不需要全部这些能力,但即使只需要其中少数几项,Cobra 也能开箱即用地提供给你。
让我们看看 Cobra 是如何组织的。
理解 Cobra 的概念
Cobra 基于命令、参数和标志的概念。基本思想是命令行应像一句话一样:
1<Application Name> <verb> <noun> <adjective>
动词对应命令,名词对应参数,标志对应形容词:
1<Application name> <command> <argument> <flag>
安装 Cobra 本身就是一个很好的例子:
1go get -u github.com/spf13/cobra/cobra
应用程序名称是 go,动词是 get,参数是 Cobra 包名 github.com/spf13/cobra/cobra,形容词是 -u,表示更新。
然而,当遇到嵌套命令、多个参数或多个标志时,这种映射很快就会失效。例如,基本的 kubectl 命令 kubectl get pods 会获取默认命名空间中的 Pod 列表,并打印一些相关信息。在这个命令中,应用程序名称是 kubectl,命令是嵌套的 get pods,没有参数或标志。如果从英语句子的角度来看,Pod 就是名词。现在,如果你想获取特定的 Pod,就需要向命令传递一个参数:kubectl get pod <pod name>。但从英语语法角度看,名词是 pod <pod name>,这既像嵌套命令又像参数。
总之,这个类比有时会被拉得太远。请以它最初提供的精神来理解——作为构建一致、易于使用和理解的命令行 API 的一般指导原则。
命令
命令是 Cobra 构建的基础概念。命令可以有子命令,每一层级的命令都可以关联一个操作。作为应用程序开发者,我们的任务是设计一组命令和子命令,作为程序的命令行接口。
Cobra 通过提供一个功能丰富的命令类型来帮助我们,该类型包含数十种不同的方法。我们将在下一课中探索其中许多方法。
命令和子命令必须按正确的顺序出现在命令行中,并且在任何参数之前。标志可以随意分布。
1command --flag1 sub-command --flag2 arg1 arg2 --flag3
参数
参数通常是命令操作的对象。它可以是文件名、URL 或其他任何内容。命令可以接受零个或多个参数。有些命令可以接收任意数量的参数,并对所有参数进行操作。例如,cat 命令接受任意数量的文件名,将它们连接起来并输出到标准输出:
1$ cat file-1 file-2 ... file-N
Cobra 区分位置参数(必须出现在特定位置)和自定义参数。Cobra 提供了多种方式来指定和验证传给命令的参数数量正确。你可以要求以下验证:
| 验证方法 | 说明 |
|---|---|
| NoArgs | 命令不接受任何参数。 |
| ArbitraryArgs | 命令接受任意参数,对数量或类型无限制。 |
| OnlyValidArgs | 命令要求所有位置参数都在 Command 的 ValidArgs 字段中。 |
| MinimumNArgs(int N) | 命令至少需要 N 个位置参数。 |
| MaximumNArgs(int N) | 命令最多允许 N 个位置参数。 |
| ExactArgs(int N) | 命令恰好需要 N 个位置参数。 |
| ExactValidArgs(int N) | 命令恰好需要 N 个位置参数,且所有位置参数都必须在 Command 的 ValidArgs 字段中。 |
| RangeArgs(min, max) | 命令允许参数数量在范围 [min..max] 内(包含边界)。 |
你还可以为参数定义自定义验证器,这些是接受所有参数并可在违反自定义验证规则时返回错误的函数。自定义验证函数的签名如下:
1func(cmd *cobra.Command, args []string) error
标志
标志是一种修改命令行为的方式。有时并不总是清楚某事物应该是一个命令、一个标志还是一个参数。例如,许多命令行程序都有一个 version 命令,而另一些则有一个 --version 标志。最终结果相同,都会显示版本信息,但从 CLI 设计角度来看,保持一致的方法更好,以避免让用户困惑。
Git 采取了高标准的做法,同时定义了版本命令和 --version 标志:
1$ git version 2git version 2.25.0
标志始终是可选的。当标志不存在时,执行默认行为。标志可以是裸标志,即不带额外信息。当标志存在时,命令的行为与不存在时不同。例如,git 的 -p 标志会将输出发送到 less 以实现分页查看。
1commit 636ba8edb8b24c79059f8dbc4fb895a8a7cac013 (HEAD -> master, origin/master, origin/HEAD) 2Author: the-gigi <the.gigi@gmail.com> 3Date: Tue Dec 31 14:09:41 2019 -0800 4 5 Sync 6 7commit 8b672d75480e68f4f5973ad7f2777409b186e833 8Author: the-gigi <the.gigi@gmail.com> 9Date: Tue Dec 31 13:03:43 2019 -0800 10 11 check that repo names are not empty 12 13commit 27f6eaadac01f1aa8a90d29768bd023c400d0602 14Author: the-gigi <the.gigi@gmail.com> 15Date: Tue Dec 31 11:25:51 2019 -0800 16 17 removed unused import 18 19...
标志可以有长名和短名。例如,git 的 -p 标志也称为 --paginate。这是一个可用性功能:长名更易读,适合脚本;而在终端输入命令时,你可能更倾向于使用短名。
为标志同时提供短名和长名是良好实践。
某些标志需要额外的信息。例如,--git-dir=<path> 用于设置 git 命令要操作的仓库路径。默认值是当前工作目录。
现在你已经理解了 Cobra 的基本概念,我们将讨论如何在程序中将其作为库使用。