乐趣区

关于程序员:要命我篡改了系统命令惊现事故竟要扣我年终奖Golangcobra

打工还是要打工的。。我最初也没收回去。

紧急解决当前,当初写复盘,大家随我看看我到底是在学习哪些内容。

(以上内容纯属虚构,如有雷同纯属巧合)

简介

之前咱们讲过 pflag 和 os.Args,当初说说 cobra 这个命令行框架。

KubernetesHugoetcd 这些出名我的项目都用 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/
    cmd/       
      root.go        
      version.go    
    utils/
      helper.go
    main.go
  • cmd指标是子命令列表,这里有一个 version 命令。
  • root.go先卖个关子,大家不要理他。
  • main.go是主程序。
  • helper是这里应用到的工具类。
  • go.mod文件我省略了。

上面的代码文件我就省略 import "github.com/spf13/cobra" 了,大家晓得就行,version.go文件:

var versionCmd = &cobra.Command{
    Use:   "version",
    Short: "version subcommand show git version info.",

    Run: func(cmd *cobra.Command, args []string) {output, err := utils.ExecuteCommand("git", "version", args...)
        if err != nil {utils.Error(cmd, args, err)
        }

        fmt.Fprint(os.Stdout, output)
    },
}

func init() {rootCmd.AddCommand(versionCmd)
}
  • 几个参数含意是子命令名称、子命令短提醒、子命令调用的办法
  • init()里把子命令加到主命令中去。

你会有纳闷 rootCmd 是哪来的吗?实际上咱们须要一个根节点,把其余命令加进来。如下是 root.go 文件。

var rootCmd = &cobra.Command {
    Use: "git",
    Short: "Git is a distributed version control system.",
    Long: `Git is a free ... 省略 `,
    Run: func(cmd *cobra.Command, args []string) {utils.Error(cmd, args, errors.New("unrecognized command"))
    },
}

func Execute() {rootCmd.Execute()
}

有没有发现这里不是 init() 而是 Execute()?这里此包惟一裸露的公开函数内容,专门供命令初始化应用。如下main.go 文件中的调用命令入口:

import "cmd"
func main() {cmd.Execute()
}

最初为了编码不便,在 helpers.go 中封装了调用内部程序和谬误处理函数,我就不开展写了,有趣味去看我的源码。

https://github.com/golang-min…

cobra 主动生成的帮忙信息,very cool

$ go run . -h
Git is a free and open source distributed version control system
designed to handle everything from small to very large projects 
with speed and efficiency.

Usage:
  git [flags]
  git [command]

Available Commands:
  completion  Generate the autocompletion script for the specified shell
  help        Help about any command
  version     version subcommand show git version info.

Flags:
  -h, --help   help for git

Use "git [command] --help" for more information about a command.

单个子命令的帮忙信息:

$ go run . version -h
version subcommand show git version info.

Usage:
  git version [flags]

Flags:
  -h, --help   help for version

调用子命令:

$ go run . version
git version 2.33.0

未辨认的子命令:

$ go run . xxx
Error: unknown command "xxx" for "git"
Run 'git --help' for usage.

应用 cobra 构建命令行时,程序的目录构造个别比较简单,举荐应用上面这种构造:

appName/
    cmd/
        cmd1.go
        cmd2.go
        cmd3.go
        root.go
    main.go

每个命令实现一个文件,所有命令文件寄存在 cmd 目录下。外层的 main.go 仅初始化 cobra。

个性

cobra 提供十分丰盛的性能:

  • 轻松反对子命令,如 app serverapp fetch 等;
  • 齐全兼容 POSIX 选项(包含短、长选项);
  • 嵌套子命令;
  • 全局、本地层级选项。能够在多处设置选项,依照肯定的程序取用;
  • 应用脚手架轻松生成程序框架和命令。

首先须要明确 3 个基本概念:

  • 命令(Command):就是须要执行的操作;
  • 参数(Arg):命令的参数,即要操作的对象;
  • 选项(Flag):命令选项能够调整命令的行为。

比方

git clone URL --bare

clone 是一个(子)命令,URL 是参数,--bare是选项。子命令咱们曾经讲过了,当初讲讲参数。

参数

比方定义命令的中央。

var cloneCmd = &cobra.Command{Use:   "clone url [destination]",
  ...
  Run: func(cmd *cobra.Command, args []string) {...

会扭转帮忙函数输入的内容。实际上还是传入字符串数组。

go run . clone -h
Clone a repository into a new directory

Usage:
  git clone url [destination] [flags]

Flags:
  -h, --help   help for clone

选项

cobra 中选项分为两种.

  • 一种是永恒选项(PersistentFlags 翻译不太规范,临时就说永恒选项),定义它的命令和其子命令都能够应用。办法是给根命令增加一个选项定义全局选项。
  • 另一种是本地选项,只能在定义它的命令中应用。

cobra 应用 pflag 解析命令行选项,上次讲过,实际上用法都是一样的。

设置永恒选项,在 root.go 根命令文件中的 init() 函数:

var(Verbose bool)
func init() {rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
}

设置本地选项,在子命令的 init() 函数:

var(Source bool)
func init() {localCmd.Flags().StringVarP(&Source, "source", "s", "","Source directory to read from")
  rootCmd.AddCommand(divideCmd)
}

两种参数都是雷同的,长选项 / 短选项名、默认值和帮忙信息。

脚手架

通过后面的介绍,咱们也看到了其实 cobra 命令的框架还是比拟固定的。这就有了工具的用武之地了,可极大地提高咱们的开发效率。

在你的 go root 下装置 cobra-cli,确保bin 目录曾经放到零碎的 path 里,之前写的文章 - 运行那一节有提到过怎么操作,不记得的回去看看哈。

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

上面咱们介绍如何应用这个生成器,先看命令帮忙:

Usage:
  cobra-cli init [path] [flags]

Aliases:
  init, initialize, initialise, create

Flags:
  -h, --help   help for init

Global Flags:
  -a, --author string    author name for copyright attribution (default "YOUR NAME")
      --config string    config file (default is $HOME/.cobra.yaml)
  -l, --license string   name of license for the project
      --viper            use Viper for configuration
  • 依据提醒子命令init,可选参数为path
  • 选项为 -a 指定作者,--config string指定 cobra-cli 本人的配置文件
  • -l指定 license--viper 应用 viper 来读取配置文件。

应用 cobra init 命令创立一个 cobra 应用程序:

$ mkdir appname
$ cd appname
$ cobra-cli init
Error: Please run `go mod init <MODNAME>` before `cobra-cli init`
$ go mod init
go: creating new go.mod: module github.com/golang-minibear2333/cmd_utils/git/appname
$ cobra-cli init
Your Cobra application is ready at
/Users/xxxx/Documents/code/go/src/github.com/golang-minibear2333/cmd_utils/git/appname
  • 先初始化mod 再初始化我的项目
  • 其中 appname 为应用程序名。生成的程序目录构造如下:
.
├── LICENSE
├── cmd
│   └── root.go
├── go.mod
├── go.sum
└── main.go

这个我的项目构造与之前介绍的完全相同,也是 cobra 举荐应用的构造。同样地,main.go也仅仅是入口。外面的英文正文十分的清晰,我一下子就看懂了用法,你也试试。

配置读取

除了命令行以外,这个库还能够用来配置读取,咱们先创立我的项目和配置文件:

mkdir cfg_load && cd_cg_load
mkdir config && touch config/cfg.yaml
cat >config/cfg.yaml <<-EOF
people:
   name: minibear2333
   age: 18
EOF

PS: linux命令不熟的能够在 Go 群里问我。

当初咱们尝试读取这个配置文件,间接应用命令来创立读取配置文件的代码。

$ cobra-cli init --viper
Your Cobra application is ready at
/Users/xxx/Documents/code/go/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 函数的内容为

var viperallCmd = &cobra.Command{
    Use:   "viperall",
    Short: "Show cfg all",
    Long: `Show the contents of the entire configuration file`,
    Run: func(cmd *cobra.Command, args []string) {fmt.Println(viper.AllSettings())
    },
}

运行,妥了。

$ go run . viperall --config=config/cfg.yaml 
Using config file: config/cfg.yaml
map[people:map[age:18 name:minibear2333]]

每次都要指定必定很麻烦,你相熟 viper 的话能够本人改一下默认文件,把我的我的项目下下来给我提交一个 pr 吧~!

子命令也能够嵌套,只须要在 init() 的时候,加到父命令里,当然也能够主动生成。

$ cobra-cli add tt -p viperallCmd
tt created at /Users/xxx/Documents/code/go/src/github.com/golang-minibear2333/cmd_utils/git/cfg_load
$ go run . viperall tt --config=config/cfg.yaml 
Using config file: config/cfg.yaml
tt called
  • 留神父命令是 viperall,然而-p 指定的时候要改为viperallCmd,因为如下(我感觉这个是个很好的奉献 pr,你能够倡议作者改一下):
var viperallCmd = &cobra.Command{

小结

  • 每个 cobra 程序都有一个根命令,能够给它增加任意多个子命令。比方咱们在 version.goinit函数中将子命令增加到根命令中。
  • 创立子命令时指定子命令名称、子命令短提醒、子命令调用的办法。
  • 三个重要概念,子命令、参数、选项。
  • 全局选项和子命令本人应用的选项。
  • cobra-cli 主动创立我的项目,主动创立配置文件读取我的项目,主动减少子命令,主动减少嵌套子命令。

举荐目录构造

.
├── LICENSE
├── cmd
│   ├── root.go
|   ├── cmd1.go
├── go.mod
├── go.sum
└── main.go

还有更多!cobra 提供了十分丰盛的个性和定制化接口,例如:

  • 设置钩子函数,在命令执行前、后执行某些操作。
  • 生成 Markdown/ReStructed Text/Man Page 格局的文档。
  • 等。本人下来学咯。

cobra 库的应用十分宽泛,很多出名我的项目都有用到,后面也提到过这些我的项目。学习这些我的项目是如何应用 cobra 的,能够从中学习 cobra 的个性和最佳实际。这也是学习开源我的项目的一个很好的路径。

本文由 mdnice 多平台公布

退出移动版