打工还是要打工的。。我最后也没发出去。
紧急处理以后,现在写复盘,大家随我看看我到底是在学习哪些内容。
(以上内容纯属虚构,如有雷同纯属巧合)
Kubernetes
、 Hugo
、 etcd
这些知名项目都用 cobra
来做命令行程序。学起来!
关于作者 spf13
,这里多说两句。 spf13
开源不少项目,而且他的开源项目质量都比较高。相信使用过 vim
的都知道 spf13-vim
,号称 vim 终极配置。可以一键配置,对于我这样的懒人来说绝对是福音。
还有他的 viper
是一个完整的配置解决方案。完美支持 JSON/TOML/YAML/HCL/envfile/Java properties
配置文件等格式,还有一些比较实用的特性,如配置热更新、多查找目录、配置保存等。还有非常火的静态网站生成器 hugo
也是他的作品牛人就是牛人。
★这个牛人 https://github.com/spf13
“
第三方库都需要先安装,后使用。下面命令安装了 cobra
生成器程序和 cobra 库:
$ go get github.com/spf13/cobra/cobra
PS: 如果出现了 golang.org/x/text
库找不到之类的错误,需要手动从 GitHub 上下载该库,再执行上面的安装命令。
现在要举的例子是让我们的程序调子命令时会透传到 git
上,用 git version
举例。目录结构如下(手动建的):
get-started/<br>    cmd/       <br>      root.go        <br>      version.go    <br>    utils/<br>      helper.go<br>    main.go
cmd
目标是子命令列表,这里有一个version
命令。root.go
先卖个关子,大家不要理他。main.go
是主程序。helper
是这里使用到的工具类。go.mod
文件我省略了。
下面的代码文件我就省略 import "github.com/spf13/cobra"
了,大家知道就行, version.go
文件:
<span class="hljs-keyword">var</span> versionCmd = &cobra.Command{<br> Use:   <span class="hljs-string">"version"</span>,<br> Short: <span class="hljs-string">"version subcommand show git version info."</span>,<br><br> Run: <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(cmd *cobra.Command, args []<span class="hljs-keyword">string</span>)</span></span> {<br>  output, err := utils.ExecuteCommand(<span class="hljs-string">"git"</span>, <span class="hljs-string">"version"</span>, args...)<br>  <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {<br>   utils.Error(cmd, args, err)<br>  }<br><br>  fmt.Fprint(os.Stdout, output)<br> },<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">init</span><span class="hljs-params">()</span></span> {<br> rootCmd.AddCommand(versionCmd)<br>}
- 几个参数含义是子命令名称、子命令短提示、子命令调用的方法
init()
里把子命令加到主命令中去。
你会有疑惑 rootCmd
是哪来的吗?实际上我们需要一个根节点,把其他命令加进来。如下是 root.go
文件。
<span class="hljs-keyword">var</span> rootCmd = &cobra.Command {<br> Use: <span class="hljs-string">"git"</span>,<br> Short: <span class="hljs-string">"Git is a distributed version control system."</span>,<br> Long: <span class="hljs-string">Git is a free ...省略
</span>,<br> Run: <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(cmd *cobra.Command, args []<span class="hljs-keyword">string</span>)</span></span> {<br>  utils.Error(cmd, args, errors.New(<span class="hljs-string">"unrecognized command"</span>))<br> },<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Execute</span><span class="hljs-params">()</span></span> {<br> rootCmd.Execute()<br>}
有没有发现这里不是 init()
而是 Execute()
?这里此包唯一暴露的公开函数内容,专门供命令初始化使用。如下 main.go
文件中的调用命令入口:
import <span class="hljs-string">"cmd"</span><br>func <span class="hljs-function"><span class="hljs-title">main</span></span>() {<br>  cmd.Execute()<br>}
最后为了编码方便,在 helpers.go
中封装了调用外部程序和错误处理函数,我就不展开写了,有兴趣去看我的源码。
★https://github.com/golang-minibear2333/cmd_utils
”cobra
自动生成的帮助信息,very cool
:
$ go run . -h<br>Git is a free and open <span class="hljs-built_in">source</span> distributed version control system<br>designed to handle everything from small to very large projects <br>with speed and efficiency.<br><br>Usage:<br>  git [flags]<br>  git [<span class="hljs-built_in">command</span>]<br><br>Available Commands:<br>  completion  Generate the autocompletion script <span class="hljs-keyword">for</span> the specified shell<br>  <span class="hljs-built_in">help</span>        Help about any <span class="hljs-built_in">command</span><br>  version     version subcommand show git version info.<br><br>Flags:<br>  -h, --<span class="hljs-built_in">help</span>   <span class="hljs-built_in">help</span> <span class="hljs-keyword">for</span> git<br><br>Use <span class="hljs-string">"git [command] --help"</span> <span class="hljs-keyword">for</span> more information about a <span class="hljs-built_in">command</span>.<br>
单个子命令的帮助信息:
$ go run . version -h<br>version subcommand show git version info.<br><br>Usage:<br>  git version [flags]<br><br>Flags:<br>  -h, --<span class="hljs-built_in">help</span>   <span class="hljs-built_in">help</span> <span class="hljs-keyword">for</span> version
调用子命令:
$ go run . version<br>git version 2.33.0
未识别的子命令:
$ go run . xxx<br>Error: unknown <span class="hljs-built_in">command</span> <span class="hljs-string">"xxx"</span> <span class="hljs-keyword">for</span> <span class="hljs-string">"git"</span><br>Run <span class="hljs-string">'git --help'</span> <span class="hljs-keyword">for</span> usage.
使用 cobra
构建命令行时,程序的目录结构一般比较简单,推荐使用下面这种结构:
appName/<br>    cmd/<br>        cmd1.go<br>        cmd2.go<br>        cmd3.go<br>        root.go<br>    main.go
每个命令实现一个文件,所有命令文件存放在 cmd
目录下。外层的 main.go
仅初始化 cobra。
cobra 提供非常丰富的功能:
- 轻松支持子命令,如
app server
,app fetch
等; - 完全兼容 POSIX 选项(包括短、长选项);
- 嵌套子命令;
- 全局、本地层级选项。可以在多处设置选项,按照一定的顺序取用;
- 使用脚手架轻松生成程序框架和命令。
首先需要明确 3 个基本概念:
- 命令(Command):就是需要执行的操作;
- 参数(Arg):命令的参数,即要操作的对象;
- 选项(Flag):命令选项可以调整命令的行为。
git <span class="hljs-built_in">clone</span> URL --bare
clone
是一个(子)命令, URL
是参数, --bare
是选项。子命令我们已经讲过了,现在讲讲参数。
比如定义命令的地方。
<span class="hljs-keyword">var</span> cloneCmd = &cobra.Command{<br> Use:   <span class="hljs-string">"clone url [destination]"</span>,<br>  ...<br>  Run: <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(cmd *cobra.Command, args []<span class="hljs-keyword">string</span>)</span></span> {<br>  ...
会改变帮助函数输出的内容。实际上还是传入字符串数组。
<span class="hljs-keyword">go</span> run . clone -h<br>Clone a repository into a <span class="hljs-built_in">new</span> directory<br><br>Usage:<br>  git clone url [destination] [flags]<br><br>Flags:<br>  -h, --help   help <span class="hljs-keyword">for</span> clone
cobra
中选项分为两种.
- 一种是永久选项(
PersistentFlags
翻译不太标准,暂时就说永久选项),定义它的命令和其子命令都可以使用。方法是给根命令添加一个选项定义全局选项。 - 另一种是本地选项,只能在定义它的命令中使用。
设置永久选项,在 root.go
根命令文件中的 init()
函数:
var(<br>  Verbose bool<br>)<br>func <span class="hljs-function"><span class="hljs-title">init</span></span>() {<br>  rootCmd.PersistentFlags().BoolVarP(&Verbose, <span class="hljs-string">"verbose"</span>, <span class="hljs-string">"v"</span>, <span class="hljs-literal">false</span>, <span class="hljs-string">"verbose output"</span>)<br>}<br>
设置本地选项,在子命令的 init()
函数:
var(<br>  Source bool<br>)<br>func <span class="hljs-function"><span class="hljs-title">init</span></span>() {<br>  localCmd.Flags().StringVarP(&Source, <span class="hljs-string">"source"</span>, <span class="hljs-string">"s"</span>, <span class="hljs-string">""</span>, <span class="hljs-string">"Source directory to read from"</span>)<br>  rootCmd.AddCommand(divideCmd)<br>}
两种参数都是相同的,长选项/短选项名、默认值和帮助信息。
通过前面的介绍,我们也看到了其实 cobra
命令的框架还是比较固定的。这就有了工具的用武之地了,可极大地提高我们的开发效率。
<span class="hljs-keyword">go</span> install github.com/spf13/cobra-cli@latest
下面我们介绍如何使用这个生成器,先看命令帮助:
Usage:<br>  cobra-cli init [path] [flags]<br><br>Aliases:<br>  init, initialize, initialise, create<br><br>Flags:<br>  -h, --help   help <span class="hljs-keyword">for</span> init<br><br>Global Flags:<br>  -a, --author <span class="hljs-keyword">string</span>    author name <span class="hljs-keyword">for</span> copyright attribution (<span class="hljs-keyword">default</span> <span class="hljs-string">"YOUR NAME"</span>)<br>      --config <span class="hljs-keyword">string</span>    config file (<span class="hljs-keyword">default</span> is $HOME/.cobra.yaml)<br>  -l, --license <span class="hljs-keyword">string</span>   name of license <span class="hljs-keyword">for</span> the project<br>      --viper            use Viper <span class="hljs-keyword">for</span> configuration
- 根据提示子命令
init
,可选参数为path
。 - 选项为
-a
指定作者,--config string
指定cobra-cli
自己的配置文件 -l
指定license
,--viper
使用viper
来读取配置文件。
使用 cobra init
命令创建一个 cobra 应用程序:
$ mkdir appname<br>$ <span class="hljs-built_in">cd</span> appname<br>$ cobra-cli init<br>Error: Please run go mod init <modname>
 before cobra-cli init
<br>$ go mod init<br>go: creating new go.mod: module github.com/golang-minibear2333/cmd_utils/git/appname<br>$ cobra-cli init<br>Your Cobra application is ready at<br>/Users/xxxx/Documents/code/go/src/github.com/golang-minibear2333/cmd_utils/git/appname<br></modname>
- 先初始化
mod
再初始化项目 - 其中
appname
为应用程序名。生成的程序目录结构如下:
.<br>├── LICENSE<br>├── cmd<br>│   └── root.go<br>├── go.mod<br>├── go.sum<br>└── main.go
这个项目结构与之前介绍的完全相同,也是 cobra
推荐使用的结构。同样地, main.go
也仅仅是入口。里面的英文注释非常的清晰,我一下子就看懂了用法,你也试试。
除了命令行以外,这个库还可以用来配置读取,我们先创建项目和配置文件:
mkdir cfg_load && cd_cg_load<br>mkdir config && touch config/cfg.yaml<br>cat >config/cfg.yaml <<-eof<br>people:<br>   name: minibear2333<br>   age: 18<br>EOF<br></-eof
PS: linux
命令不熟的可以在 Go
群里问我。
现在我们尝试读取这个配置文件,直接使用命令来创建读取配置文件的代码。
$ cobra-cli init --viper<br>Your Cobra application is ready at<br>/Users/xxx/Documents/code/<span class="hljs-keyword">go</span>/src/github.com/golang-minibear2333/cmd_utils/git/cfg_load
现在就创建了一个默认配置文件为 $HOME/.cfg_load.yaml
的命令行程序,而我们之前放在了另一个位置,所以启动的时候需要指定一下。
$ go run . --config==config/cfg.yaml
配置文件就成功载入了,现在你就可以用 viper
在需要的地方读取配置了。
为了展示一下配置是否成功读取,继续用 cobra-cli
来创建一个子命令。
$ cobra-cli add viperall
修改此子命令 Run
函数的内容为
<span class="hljs-keyword">var</span> viperallCmd = &cobra.Command{<br> Use:   <span class="hljs-string">"viperall"</span>,<br> Short: <span class="hljs-string">"Show cfg all"</span>,<br> Long: <span class="hljs-string">Show the contents of the entire configuration file
</span>,<br> Run: <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(cmd *cobra.Command, args []<span class="hljs-keyword">string</span>)</span></span> {<br>  fmt.Println(viper.AllSettings())<br> },<br>}
运行,妥了。
$ go run . viperall --config=config/cfg.yaml <br>Using config file: config/cfg.yaml<br>map[people:map[age:18 name:minibear2333]]
每次都要指定肯定很麻烦,你熟悉 viper
的话可以自己改一下默认文件,把我的项目下下来给我提交一个 pr
吧~!
子命令也可以嵌套,只需要在 init()
的时候,加到父命令里,当然也可以自动生成。
$ cobra-cli add tt -p viperallCmd<br>tt created at /Users/xxx/Documents/code/go/src/github.com/golang-minibear2333/cmd_utils/git/cfg_load<br>$ go run . viperall tt --config=config/cfg.yaml <br>Using config file: config/cfg.yaml<br>tt called
- 注意父命令是
viperall
,但是-p
指定的时候要改为viperallCmd
,因为如下(我觉得这个是个很好的贡献pr,你可以建议作者改一下):
<span class="hljs-keyword">var</span> viperallCmd = &cobra.Command{
- 每个 cobra 程序都有一个根命令,可以给它添加任意多个子命令。比如我们在
version.go
的init
函数中将子命令添加到根命令中。 - 创建子命令时指定子命令名称、子命令短提示、子命令调用的方法。
- 三个重要概念,子命令、参数、选项。
- 全局选项和子命令自己使用的选项。
cobra-cli
自动创建项目,自动创建配置文件读取项目,自动增加子命令,自动增加嵌套子命令。
推荐目录结构
还有更多! cobra
提供了非常丰富的特性和定制化接口,例如:
- 设置钩子函数,在命令执行前、后执行某些操作。
- 生成 Markdown/ReStructed Text/Man Page 格式的文档。
- 等。自己下来学咯。
cobra
库的使用非常广泛,很多知名项目都有用到,前面也提到过这些项目。学习这些项目是如何使用 cobra
的,可以从中学习 cobra
的特性和最佳实践。这也是学习开源项目的一个很好的途径。
Original: https://blog.csdn.net/BTnode/article/details/128717698
Author: 机智的程序员小熊
Title: 要命!我篡改了系统命令惊现事故,竟要扣我年终奖-Golang-cobra
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/811878/
转载文章受原作者版权保护。转载请注明原作者出处!