1、cobra使用
包含两个模块:
(1)先用Generator自动生成模板,
(2)再依据Cobra Library修改模板,设置自己的参数。
1、Generator使用
注意:由于代码是自动生成的,最好不要在自己项目中随便用
这生成命令,因为一不小心就把你的main.go全部覆盖了!!!
建议:建个小demo,配置好了再粘到你的项目中的cli相关目录下就行。
1.1 命令生成cobra代码
(1)新建Go项目并初始化
手动创建你的项目文件夹cobra_test1
go mod init cobra_test1
//go项目的初始化,生成go.mod
坑1:go mod init github.com/spf13/cobra_test1
【不要用这个命令!】
这个命令的含义是,项目名称是"github.com/spf13/cobra_test1",而不是"cobra_test1",而默认的main.go中引用的是后者,因此找不到,飙红!
一开始我就这么用的,坑死了,这玩意会在cobra_test1的文件夹外面生成.sum文件,导致引入路径错误!
当时问题是"cmd"引入飙红,只发现了莫名其妙在文件夹外出现的.sum文件,但不知道哪步错了,死活找不到原因。
只好全删了重新按照普通go项目的init来。
当时录了屏,第二天看录屏的时候才发现,比坑2还要隐蔽!
(2)安装cobra generator
go install github.com/spf13/cobra-cli@latest
//安装之后cobra-cli命令才能使用
(3)执行cobra的初始化
cobra-cli init
// 此命令自动生成的文件有:go.sum,LICENSE,main.go,cmd/root.go
自动生成的目录结构如下:
坑2:cobra-cli init cobra_test1
【不要用这个命令!】
不要在后面跟cobra_test1,否则其生成的目录结构如下:
此时main.go中import cmd 时会飙红,因为cmd的路径变成了嵌套的,
而默认是不嵌套的"cobra_test1/cmd",需要改成"cobra_test1/cobra_test1/cmd"
(4)添加command
假如你想在项目中执行以下命令:
cobra_test1 serve // serve和config都是root的子命令
cobra_test1 config
cobra_test1 config create // create是config的子命令
cobra_test1 config delete // delete是config的子命令
那么你应该在cobra_test1根目录下,继续执行以下命令:
cobra-cli add serve
cobra-cli add config
cobra-cli add create -p 'configCmd' //注意,这里是配置"config"的子命令,因此要用`-p 'configCmd'`
cobra-cli add delete -p 'configCmd' //同上
执行后会在cmd下自动生成serve.go config.go create.go delete.go四个文件,
目录如下:
1.2 生成的cobra代码含义
生成的文件有:go.sum,LICENSE,main.go,cmd/root.go cmd/serve.go cmd/config.go cmd/create.go cmd/delete.go
go.sum里面是cobra的依赖包,不用管;LICENSE是空的,暂时不用管;
main.go调用cmd.Execute()函数:
package main
import "cobra_test1/cmd"
func main() {
cmd.Execute()
}
cmd/root.go包含三部分:
(1)变量rootCmd定义,表示base command
(2)init()函数,定义flags和configuration settings/handle configuration
(3)Execute() 函数,调用rootCmd.Execute()
package cmd
import (
"os"
"github.com/spf13/cobra"
)
// rootCmd表示base command,调用时没有任何subcommands
var rootCmd = &cobra.Command{
Use: "cobra_test1",
Short: "A brief description of your application",
Long: `……`,
}
// Execute添加所有child commands到root command,并且适当地设置flags.
// Execute在main.main()中调用,仅需要在rootCmd上执行一次.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
//定义flags和configuration settings
func init() {
// persistent flags, which, if defined here, will be global for your application.
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra_test1.yaml)")
// local flags, which will only run when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
cmd/serve.go包含两部分:
(1)变量serveCmd定义,表示base command
(2)init()函数,定义flags和configuration settings/handle configuration
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var serveCmd = &cobra.Command{
Use: "serve",
Short: "简述你的command",
Long: `更长的描述,可跨多行,可以包含你command的案例和使用,例如:
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.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("serve called")
},
}
func init() {
rootCmd.AddCommand(serveCmd)
}
1.3 运行代码
go run main.go serve // 执行serveCmd.Run:func()的内容,输出:serve called
go run main.go config // 输出:config called
注意,由于只是一个demo项目,main函数只有一个execute函数的调用,然后就结束了,因此每次执行一条命令就结束了。
指令运行是一次性的。
故,每次都需要在前面带上"go run main.go"
如果想要main不结束,需要借助多协程,
把这个cli加入自己项目的方法,就是在相应指令的Run:func()中执行相应的函数,
而这些函数一般都是多协程的,程序就不会直接结束了。
参考:
https://www.freesion.com/article/80051415478/
2、使用Cobra Library设置参数
参数两种配置方式:
(1)用command+args方式,不需要flag名称
需要考虑args的过滤!
我的理解:args是那种没有默认值的,可填可不填的参数。而如果要用默认值,最好用flag
git clone "xxx" 后面跟的就是一个arg,实际上是地址。
conn "127.0.0.1:8888" 和服务器建立ws连接,地址格式"127.0.0.1:8888"
(2)用--flag value方式,需要flag名称,即全局/局部的flags。
//这个还没设计好,按理说应该把cyclic和period作为args,而最后的--table作为flag比较好吧??但是多个值又怎么处理呢??
//目前下面这种是三个flag的,过于复杂了。
send [--cyclic] [--period] --table 向服务器发送1203表的数据【send -c -p=100 1203/1204/1205, 1206 1207 】
-t, --table int 向服务器发送的数据表号,必须是整型 (默认 --table=1203 )【多个用“/”隔开怎么设置?】
-c, --cyclic bool 区分周期/非周期数据表,true为周期,false为非周期(默认 -c=true/1或 -c)【如果没有这个参数表示非周期,可是这怎么设置?】
-p, --period int 周期数据的发送周期,单位是ms
参数分类、含义:2种,全局persistent flags和局部local flags
在哪里:在对应命令的init()函数中
那些参数可配置、如何配置:
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra_test1.yaml)")
//函数定义如下:
func (f *FlagSet) StringVar(p *string, name string, value string, usage string)
StringVar定义一个string flag,共四个参数:
name:flag的名称
value:flag的默认值
usage:flag的用法描述
p:指针,指向一个string变量,这个变量存储flag的值
问题:
(1)p这个指针对应的变量在哪里定义?
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
参考:
https://cobra.dev/
https://github.com/spf13/cobra-cli/blob/main/README.md 【坑1 2】
https://github.com/spf13/cobra/blob/main/user_guide.md
https://www.cnblogs.com/niuben/p/13886555.html 【英文教程太难搞了,绕弯弯也不说参数到底是什么语法,对应命令的案例。还是这个中文教程清晰明了】
https://www.thorsten-hans.com/lets-build-a-cli-in-go-with-cobra/ 【带命令使用的案例】
https://umarcor.github.io/cobra/ 【这个教程也很好,有命令使用案例!而且左右分开很清晰】
https://github.com/divrhino/studybuddy 【有视频+代码,但是仍然不知道怎么交互的】
2、总结
(1)有上下文可交互的工具grumble,有空试试
参考:
https://zhuanlan.zhihu.com/p/298639762
(2)spf13/cobra 和 urfave/cli(提到的所有cli工具),都是一次性的程序,没有上下文。也就是说,其交互方式,不能连续输入指令,每输入一次指令,相当于重启一次程序。
如果需要记录上下文,需要手动添加os.Stdin,读入参数后按空格拆分,手动判断到底是哪个命令,从而执行该函数。这样的话,连输入和分支的逻辑都需要自己写,他这个cli的意义就不大了,仅仅是辅助设计命令,不要也罢。
关于c-bata/go-prompt:
现在唯一流行的是 c-bata/go-prompt 这个库,它做到了类 python prompt toolkits 的体验,也就是能够很美观地自动补全。
一个命令行工具基于 go-prompt 仍要再堆不少代码,才可在真实世界使用。所以 go-prompt 可谓一个专精的库,而非完整的框架。
——————
更多cobra:
https://juejin.cn/post/6975039037673472013
https://www.jianshu.com/p/99aae91587af
https://piaohua.github.io/post/golang/20220807-cobra-cli/
https://www.jianshu.com/p/790dc1171bbf
https://juejin.cn/post/6924541628031959047
作者:西伯尔
出处:http://www.cnblogs.com/sybil-hxl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。