文章目录
- 一、背景
- 二、命令框架
-
- 1.flag库/pflag库
- 2.cobra库
-
- 1.命令基础
- 2.cobra基础用法
-
- 1.初始化
- [2.git clone url -b {branch}命令](#2.git clone url -b {branch}命令)
- 三、总结
一、背景
制作一个命令行工具是一个常见的需求,如何选择一种合适的编程的语言实现?一方面,命令行工具的开发需要考虑开发便捷程度,因为命令行工具大多是用来运维的,一方面,命令行工具的使用形态应该是一个二进制工具。因此,使用go语言开发是一个优秀的选择。那么,go语言中有哪些成熟的命令行工具的框架供使用呢?下面介绍典型的框架 flag/pflag 、cobra库的基本用法。
二、命令框架
1.flag库/pflag库
flag 库是官方的典型库,能够让用户简单快捷地设置各种参数,下面是一个典型的例子。
代码如下(示例):
go
// 导入省略
func main() {
// 定义命令行参数
var (
name string
age int
married bool
weight float64
)
// 注册参数
flag.StringVar(&name, "name", "", "用户名")
flag.IntVar(&age, "age", 0, "年龄")
// 特别注意,bool不需要传值,仅仅使用--married即可
flag.BoolVar(&married, "married", false, "是否已婚")
flag.Float64Var(&weight, "weight", 0.0, "体重(kg)")
// 解析命令行参数
flag.Parse()
//flag.Parse()实际上是解析 os.Args[1:]的快捷方式,即flag.CommandLine.Parse(os.Args[1:])
// 而 os.Args[1:] 则是输入参数
// 使用参数
fmt.Printf("用户信息:\n")
fmt.Printf(" 姓名: %s\n", name)
......
}
flag库可以满足基础的需要,但是也存在一些问题,比如不能直接支持省略参数,必要参数支持等。因此,pfag库诞生了,这个库支持的功能更加全面,而且是可以兼容flag库的代码。其用法和flag几乎一摸一样,仅仅是在函数后面加了一个大写的P作为区分。
go
// pflag 支持短选项
pflag.BoolVarP(&help, "help", "h", false, "显示帮助")
pflag.BoolVarP(&version, "version", "V", false, "显示版本")
pflag.BoolVarP(&verbose, "verbose", "v", false, "详细输出")
pflag.StringVarP(&config, "config", "c", "config.yaml", "配置文件")
pflag.IntVarP(&port, "port", "p", 8080, "端口号")
2.cobra库
cobra库是一个强大的命令行框架,并且被广泛使用,在具体了解cobra的用法之前,我们需要对命令的规范有简单的认识。
1.命令基础
一个命令一般由子命令(可以不存在)和参数组成。
一个命令一般由子命令(可以不存在)和参数组成。
cp命令(功能仅仅是复制),没有子命令,其后续都是参数。
git命令(功能很多,因此需要划分子命令进行区分) ,其后续可以接clone、commit等子命令。
从使用方法上来分 ,命令的参数则分为位置参数和和标志参数:
位置参数的位置不能随意变化,比如 cp src.xtx dest.txt,如果src.txt 和 dest.txt 的位置发生变化,那么复制谁到哪里就完全变了。
标志参数又可以分为带值的标志参数和不带值的标志参数
命令 cp -r /tmp/* /sf/ ,其中的-r表示递归复制,可以放到cp后面,也可以放到最后,-r就是不带值参数,类似一个布尔开关

命令 find / -name my.txt ,其中的my.txt 就是-name参数的值,而根目录/ 则是find 命令的位置参数。

从功能上划分 ,命令的参数还可以分为全局参数和局部参数,一般是包含子命令的命令才有这个区分,某个命令设置全局参数,那么其所有子命令都将有这个参数,一般都是根命令设置全局参数。
代码如下(示例):
2.cobra基础用法
前文我们已经对命令的规范有了基础的了解,接下来可以使用cobra来完成一个模仿git命令的案例来学习前文提到的各种用法。注意:命令的各种参数子命令的命名是具有强语义性的。
1.初始化
因为命令具有强规范性,其代码也是如果,因此,cobra框架提供了一个cli脚手架,可以供用户生成模板代码,每条命令都可以根据脚手架的命令去生成,可以节省用户手动写模板代码的工作量。
bash
# 安装最新版本
go install github.com/spf13/cobra-cli@latest
# 验证安装
cobra-cli --help
下面让我们实现git --debug 命令的功能、git clone url -b {branch}命令
bash
PS D:\GoTest\cobra-example> cobra-cli init
Your Cobra application is ready at
效果:
bash
simple-tool/
├── cmd/
│ ├── root.go # 根命令
├── main.go
└── go.mod
main.go 文件
go
// 省略导入文件头部
func main() {
cmd.Execute()
}
root.go 文件
go
// 省略导入文件头部
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "cobra-example",
Short: "A brief description of your application",
Long: `A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
// Uncomment the following line if your bare application
// has an action associated with it:
// Run: func(cmd *cobra.Command, args []string) { },
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra-example.yaml)")
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
将init中的说明阅读后,可知,每个子命令都可以有全局参数和局部参数的设置,局部参数只对当前命令生效,全局参数则对其后所有子命令都生效。每个参数设置的方法的根源其实就是来自于前文提到的pflag库和flag库(表面略有不同),因此,在此不再详细展示,只关注整体的用法。
我们可以设置一个--version参数和--debug参数来进行示例,一个是展示工具的版本号,放在父命令(工具名称后的第一个子命令相对于后续的子命令可以被称为父命令)下的局部参数即可,其实默认携带的--help参数就是全局参数。
go
rootCmd.PersistentFlags().BoolP("debug", "d", false, "Enable for debug logs.")
rootCmd.Flags().BoolP("version", "V", false, "Version Info.")
增加上述的标志,并将cobra定义中的run函数放开并添加逻辑即可,同时增加一个PersistentPreRun,这个将在所有子命令前执行
go
// 🎯 关键:在所有命令之前执行
PersistentPreRun: func(cmd *cobra.Command, args []string) {
// 配置全局 logger
enabled, _ := cmd.Flags().GetBool("debug")
fmt.Println(enabled)
if enabled {
fmt.Println("打开debug级别日志")
}
},
Run: func(cmd *cobra.Command, args []string) {
// 处理 --version
version, _ := cmd.Flags().GetBool("version")
if version {
fmt.Println("1.0.0")
os.Exit(0)
}
enabled, _ := cmd.PersistentFlags().GetBool("debug")
if enabled {
fmt.Println("打开debug级别日志")
}
// 正常逻辑
if len(args) == 0 {
cmd.Help()
}
},
bash
PS D:\GoTest\cobra-example> go run .\main.go --help
A longer description that ......
Usage:
cobra-example [flags]
Flags:
-d, --Debug Enable for debug logs.
-h, --help help for cobra-example
-t, --toggle Help message for toggle
PS D:\GoTest\cobra-example> go run .\main.go --version
1.0.0
PS D:\GoTest\cobra-example> go run .\main.go --debug
打开debug级别日志
2.git clone url -b {branch}命令
使用cobra-cli来增加一个子命令clone子命令
bash
PS D:\GoTest\cobra-example> cobra-cli add clone
clone created at D:\GoTest\cobra-example
得到的结果是在root.go 同级目录下生成一个clone.go文件,内容和root.go 差距不大,主要差别是在init函数有一行代码,rootCmd.AddCommand(cloneCmd),表示了层级关系,如果要在clone下生成一个子命令呢,那么需要命令 cobra-cli add clonechild --parent clone 即可
注意:git clone url 中的url不是子命令,他是可以变化的,是用户输入的位置参数。
go
// 省略导入包
// cloneCmd represents the clone command
var cloneCmd = &cobra.Command{
Use: "clone",
Short: "A brief description of your command",
Long: `A longer description that .......`,
Run: func(cmd *cobra.Command, args []string) {
var url string
if len(args) > 0 {
url = args[0]
} else {
fmt.Println("缺少url参数")
}
branch, _ := cmd.Flags().GetString("branch")
if branch != "" {
fmt.Printf("clone url (%s) from branch (%s)", url, branch)
os.Exit(0)
}
fmt.Printf("clone url (%s) from master", url)
},
}
func init() {
rootCmd.AddCommand(cloneCmd)
cloneCmd.Flags().StringP("branch", "b", "", "branch Info.")
}
结果展示:
bash
PS D:\GoTest\cobra-example> go run .\main.go --debug clone http:123.com -b develop/6.12.0
打开debug级别日志
clone url (http:123.com) from branch (develop/6.12.0)
三、总结
Cobra是一个十分强大的命令行工具,可以适配各种需求的命令,是用户设计cli工具的首选。本文先介绍了flag/pflag 库的基础用法,单功能的命令工具是足以使用的。其次,介绍了命令中子命令、参数的各种类型和规范,帮助用户了解命令的基础知识。最后,介绍了cobra工具的最基础用法,用户了解后可以开发大部分的命令行工具。