5. 子命令与Cobra命令树的实现
在 cobra 中, 每个 命令 都是独立的。 通过 parent.AddCommand(children)
的形式进行串连。
var root = &cobra.Command{}
var child = &cobra.Command{}
func init() {
root.AddCommand(child)
}
没了, 应用上就这么多。
如果你用过 gin 的路由树的话, 可能会对 cobra 的命令树实现更为深刻。
删除多余的结构, cobra 节点 就是下面这样的。
type Command struct {
// 子命令
commands []*Command
// 父命令
parent *Command
}
当进行 命令树 组合的时候, 是通过 领养/挂靠 的方式实现的。
// AddCommand adds one or more commands to this parent command.
func (c *Command) AddCommand(cmds ...*Command) {
for i, x := range cmds {
if cmds[i] == c {
panic("Command can't be a child of itself")
}
// 为子命令设置父命令(认亲)
cmds[i].parent = c // (1)
// 省略
// 添加传入的子命令
c.commands = append(c.commands, x) // (2)
}
}
(1)
在我们可以在任意节点调用 Execute
方法, 这个调用会通过 递归 找到最上层的 根 节点。
func (c *Command) Execute() error {
_, err := c.ExecuteC()
return err
}
// ExecuteC executes the command.
func (c *Command) ExecuteC() (cmd *Command, err error) {
// Regardless of what command execute is called on, run on Root only
// 递归寻找最上层
if c.HasParent() {
return c.Root().ExecuteC()
}
}
再来看一次 Command 结构体定义
type Command struct {
// 子命令
commands []*Command
// 父命令
parent *Command
}
(2)
通过将传入的命令保存到 []*Command
切片中, 实现了命令树的 组合 。
c.commands = append(c.commands, x) // (2)
且同时 实现了 同一个命令挂载到多个父命令上
root.AddCommand(sub1, sub2) // (a)
sub1.AddCommand(sub2) // (b)
commands
是一个切片, 用于保存 挂载 上来的子命令。 但是, 这里需要注意, 由于 *Command
是指针类型, 意味着在任何修改, 都可能影响其他调用的地方。
对于 sub2
而言, (a)
处父节点是 root
。 (b)
处父节点是 sub1
。 最终父节点是 sub1
。
但不影响 sub2
同时是 root
和 sub1
的子命令。
总体来说, cobra 命令节点 独立而又统一