searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Go Cobra源码分析——ExecuteC()

2023-09-26 09:42:02
26
0

Cobra 建立在 commands、arguments 和 flags 结构之上。commands 代表命令,arguments 代表非选项参数,flags 代表选项参数(也叫标志)。commands分为主命令和子命令,两者由AddCommand()构成了一个多叉树。并以ExecuteC()函数为入口执行。

Command.AddCommand()

  1. cmds[i].parent = c: 子 Command 通过 parent 字段指向父 Command
  2. c.commands = append(c.commands, x): 父 Command通过 commands 列表关联其所有的子 Command
  3. 从这个结构可以看到所有的 Command 构成了一个多叉树

command.go文件下的ExecuteC() 开始

  1. 新建c.ctx= context.Background() ,属于空白的context(上下文)

  2. 判断是不是子命令(无论主命令还是子命令,都会以其主命令执行,基本不可能)

  3. windows hook(好像是windows CLI的易用性插件)

  4. 添加 help 子命令:c.InitDefaultHelpCmd()

    1. 如果没有子命令就返回,如果没有help就新建一个默认的,反之则什么也不做(其实是先remove然后add help子命令)
  5. 添加参数

    1. args = os.Args[1:] 将主命令后的所有命令加入到args
  6. 添加 completion 子命令: c.InitDefaultCompletionCmd() (用于生成命令行自动完成的脚本,以便在用户使用命令行工具时可以快速地补全命令、子命令、标志和参数等)

  7. 找到要执行的子命令: cmd, flags, err = c.Find(args) 。递归的将命令行参数中的flag剔除,只保留包含子命令的部分,最后返回的是没有子命令的最后一个子命令cmd

    1. stripFlags 函数负责剔除flag
    2. findNext 函数负责根据名称找到Command 中对应的子命令
  8. 执行子命令: err = cmd.execute(flags) (最后一个子命令)

    1. ParseFlags()解析flags
      1. PersistentFlags()获取全局标志(全局标志是在根命令和所有子命令中都可以使用的标志,它们对整个应用程序都是全局的,不受子命令的影响。)
      2. err := c.Flags().Parse(args)根据命令,全局标志和参数等信息,找到与之对应的value ,继而确定函数。
      3. helpVal, err := c.Flags().GetBool("help")判断是否有help
    2. 执行c.PreRun()
    3. 执行本命令及其父命令的PersistentPreRun()
    4. 执行Run()
    5. 执行本命令及其父命令的PersistentPostRun()

注意:在cobra中PreRun()PersistentPreRun()Run()PersistentPostRun() 都是钩子函数,使用的时候需要在定义command时创建,其中Run() 是必须创建的。

总结

总的来说,入口虽然是root.goExecute() ,但实际上是递归到最后一个子命令的Run() 。子命令没有Execute() AddCommand() 函数可以建立以rootCmd为根节点的多叉树,可以找到所有的父命令和子命令。

0条评论
0 / 1000
y****n
4文章数
0粉丝数
y****n
4 文章 | 0 粉丝
原创

Go Cobra源码分析——ExecuteC()

2023-09-26 09:42:02
26
0

Cobra 建立在 commands、arguments 和 flags 结构之上。commands 代表命令,arguments 代表非选项参数,flags 代表选项参数(也叫标志)。commands分为主命令和子命令,两者由AddCommand()构成了一个多叉树。并以ExecuteC()函数为入口执行。

Command.AddCommand()

  1. cmds[i].parent = c: 子 Command 通过 parent 字段指向父 Command
  2. c.commands = append(c.commands, x): 父 Command通过 commands 列表关联其所有的子 Command
  3. 从这个结构可以看到所有的 Command 构成了一个多叉树

command.go文件下的ExecuteC() 开始

  1. 新建c.ctx= context.Background() ,属于空白的context(上下文)

  2. 判断是不是子命令(无论主命令还是子命令,都会以其主命令执行,基本不可能)

  3. windows hook(好像是windows CLI的易用性插件)

  4. 添加 help 子命令:c.InitDefaultHelpCmd()

    1. 如果没有子命令就返回,如果没有help就新建一个默认的,反之则什么也不做(其实是先remove然后add help子命令)
  5. 添加参数

    1. args = os.Args[1:] 将主命令后的所有命令加入到args
  6. 添加 completion 子命令: c.InitDefaultCompletionCmd() (用于生成命令行自动完成的脚本,以便在用户使用命令行工具时可以快速地补全命令、子命令、标志和参数等)

  7. 找到要执行的子命令: cmd, flags, err = c.Find(args) 。递归的将命令行参数中的flag剔除,只保留包含子命令的部分,最后返回的是没有子命令的最后一个子命令cmd

    1. stripFlags 函数负责剔除flag
    2. findNext 函数负责根据名称找到Command 中对应的子命令
  8. 执行子命令: err = cmd.execute(flags) (最后一个子命令)

    1. ParseFlags()解析flags
      1. PersistentFlags()获取全局标志(全局标志是在根命令和所有子命令中都可以使用的标志,它们对整个应用程序都是全局的,不受子命令的影响。)
      2. err := c.Flags().Parse(args)根据命令,全局标志和参数等信息,找到与之对应的value ,继而确定函数。
      3. helpVal, err := c.Flags().GetBool("help")判断是否有help
    2. 执行c.PreRun()
    3. 执行本命令及其父命令的PersistentPreRun()
    4. 执行Run()
    5. 执行本命令及其父命令的PersistentPostRun()

注意:在cobra中PreRun()PersistentPreRun()Run()PersistentPostRun() 都是钩子函数,使用的时候需要在定义command时创建,其中Run() 是必须创建的。

总结

总的来说,入口虽然是root.goExecute() ,但实际上是递归到最后一个子命令的Run() 。子命令没有Execute() AddCommand() 函数可以建立以rootCmd为根节点的多叉树,可以找到所有的父命令和子命令。

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0