Cobra 建立在 commands、arguments 和 flags 结构之上。commands 代表命令,arguments 代表非选项参数,flags 代表选项参数(也叫标志)。commands分为主命令和子命令,两者由AddCommand()
构成了一个多叉树。并以ExecuteC()
函数为入口执行。
Command.AddCommand()
cmds[i].parent = c
: 子 Command 通过 parent 字段指向父 Commandc.commands = append(c.commands, x)
: 父 Command通过 commands 列表关联其所有的子 Command- 从这个结构可以看到所有的 Command 构成了一个多叉树
command.go文件下的ExecuteC()
开始
-
新建
c.ctx= context.Background()
,属于空白的context(上下文)
-
判断是不是子命令(无论主命令还是子命令,都会以其主命令执行,基本不可能)
-
windows hook(好像是windows CLI的易用性插件)
-
添加 help 子命令:
c.InitDefaultHelpCmd()
- 如果没有子命令就返回,如果没有help就新建一个默认的,反之则什么也不做(其实是先remove然后add help子命令)
-
添加参数
args = os.Args[1:]
将主命令后的所有命令加入到args
中
-
添加 completion 子命令:
c.InitDefaultCompletionCmd()
(用于生成命令行自动完成的脚本,以便在用户使用命令行工具时可以快速地补全命令、子命令、标志和参数等) -
找到要执行的子命令:
cmd, flags, err = c.Find(args)
。递归的将命令行参数中的flag
剔除,只保留包含子命令的部分,最后返回的是没有子命令的最后一个子命令cmdstripFlags
函数负责剔除flag
findNext
函数负责根据名称找到Command
中对应的子命令
-
执行子命令:
err = cmd.execute(flags)
(最后一个子命令)ParseFlags()
解析flags
PersistentFlags()
获取全局标志(全局标志是在根命令和所有子命令中都可以使用的标志,它们对整个应用程序都是全局的,不受子命令的影响。)err := c.Flags().Parse(args)
根据命令,全局标志和参数等信息,找到与之对应的value
,继而确定函数。helpVal, err := c.Flags().GetBool("help")
判断是否有help
- 执行
c.PreRun()
- 执行本命令及其父命令的
PersistentPreRun()
- 执行
Run()
- 执行本命令及其父命令的
PersistentPostRun()
注意:在cobra中PreRun()
、PersistentPreRun()
、Run()
、PersistentPostRun()
都是钩子函数,使用的时候需要在定义command时创建,其中Run()
是必须创建的。
总结
总的来说,入口虽然是root.go
的Execute()
,但实际上是递归到最后一个子命令的Run()
。子命令没有Execute()
。AddCommand()
函数可以建立以rootCmd
为根节点的多叉树,可以找到所有的父命令和子命令。