如何用 __complete 隐藏命令调试 Cobra 的 shell 动态补全代码?
如何用 __complete 隐藏命令调试 Cobra 的 shell 动态补全代码【免费下载链接】cobraA Commander for modern Go CLI interactions项目地址: https://gitcode.com/GitHub_Trending/co/cobra你在用 Cobra 开发的 Go CLI 程序里注册了动态补全函数ValidArgsFunction或RegisterFlagCompletionFunc()但 shell 里按 Tab 得到的候选项不对候选缺失、混入了文件路径、或者补全行为不符合预期。反复在 shell 里按 Tab 很难定位问题因为你看不到你的 Go 函数实际返回了什么。Cobra 提供了一个隐藏命令__complete它就是各 shell 补全脚本内部请求补全选项的入口——直接调用它可以在终端里原样观察到补全函数返回的候选项和指令directive而无需依赖 shell 环境。本文基于 Cobra Shell Completions 文档 和 源码 介绍这套调试方法适用于任何使用 Cobra 动态补全机制bash V2、zsh、fish、PowerShell 脚本的 Go 程序。先看懂 __complete 的输出约定从源码看__complete是 Cobra 注册的隐藏命令常量ShellCompRequestCmd __completeinitCompleteCmd()创建的命令Use为__complete [command-line]Hidden: true不出现在 help 中并且要求至少一个参数MinimumNArgs(1)。它的输出契约是stdout每一行一个候选补全项最后一行是:指令编号例如:4stderr一行便于人读的信息例如Completion ended with directive: ShellCompDirectiveNoFileComp。另外一个关键细节getCompletions()会把命令行中的最后一个参数视为用户还没打完的片段toComplete其余部分才作为已完成的命令行去解析。这就是为什么调试命令最后总是跟一个片段而还没输入任何字符时要跟一个空字符串。直接调用 __complete 模拟 Tab 补全文档用helm程序作为示例。对你自己的程序把helm换成你的可执行文件名即可。调试名词名词参数补全当你补全helm status后面的 release 名、且已经输入了har时等价命令是$ helm __complete status har harbor :4 Completion ended with directive: ShellCompDirectiveNoFileComp # This is on stderr以上输出为文档示例Shell Completions 文档 · Debugging。harbor是 stdout 上的候选项:4是 directive 编号。重要如果被补全的名词还没有输入任何字母必须传一个空参数$ helm __complete status harbor notary rook thanos :4 Completion ended with directive: ShellCompDirectiveNoFileComp # This is on stderr同样是文档示例这里假设集群中的 Helm release 为harbor、notary、rook、thanos。调试 flag 值补全flag 值的补全调试方式相同把 flag 名放在命令行里、flag 值位置放片段$ helm __complete status --output json table yaml :4 Completion ended with directive: ShellCompDirectiveNoFileComp # This is on stderr如果 debug 的目标是带 flag 的名词补全注意文档说明Cobra 会在解析完命令行上所有 flag 和参数之后才调用你注册的ValidArgsFunction因此不需要在函数里自己解析 flag——把已经输入的 flag 原样放进传给__complete的命令行即可。如何读懂 directive最后一行的:编号对应你的补全函数返回的cobra.ShellCompDirective。文档中出现的编号对应关系是:4↔ShellCompDirectiveNoFileComp:0↔ShellCompDirectiveDefault。指令本身是位域bit field可以用按位或组合例如cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp。各指令的含义引自 Shell Completions 文档指令含义ShellCompDirectiveDefault提供补全后由 shell 执行默认行为意味着其余指令均不生效ShellCompDirectiveError发生了错误补全应被忽略ShellCompDirectiveNoSpace即使只有一个补全也不在补全后追加空格ShellCompDirectiveNoFileComp即使没有提供补全也不做文件补全ShellCompDirectiveFilterFileExt返回的补全用作文件扩展名过滤器如只补全*.json、*.yamlShellCompDirectiveFilterDirs只提供目录名返回单个目录名可限定在该目录内搜索ShellCompDirectiveKeepOrdershell 保持补全提供的顺序排查时先看 stderr 那行指令名是否等于你函数返回的指令如果本该返回ShellCompDirectiveNoFileComp却出现了文件路径补全说明函数返回的指令不对或者函数根本没被注册/没被执行到未注册补全函数时 Cobra 默认使用ShellCompDirectiveDefault会触发 shell 的文件名补全。在 Go 补全代码中埋点追踪文档特别强调不要在补全代码里留下直接打印到 stdout 的调试输出因为它们会被补全脚本当成补全候选项解析。应改用 Cobra 提供的调试函数Shell Completions 文档 · Debugging// Prints to the completion script debug file (if BASH_COMP_DEBUG_FILE // is set to a file path) and optionally prints to stderr. cobra.CompDebug(msg string, printToStdErr bool) cobra.CompDebugln(msg string, printToStdErr bool) // Prints to the completion script debug file (if BASH_COMP_DEBUG_FILE // is set to a file path) and to stderr. cobra.CompError(msg string) cobra.CompErrorln(msg string)以文档中的helm status示例函数为基础示例getReleasesFromCluster为你的补全数据来源函数可以在函数入口加一行埋点ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) { if len(args) ! 0 { return nil, cobra.ShellCompDirectiveNoFileComp } // 示例埋点确认函数被调用及 toComplete 的实际取值 cobra.CompDebugln(status completion, toComplete: toComplete, true) return getReleasesFromCluster(toComplete), cobra.ShellCompDirectiveNoFileComp }配合环境变量把日志落到文件路径可换成任意可写路径这里是示例export BASH_COMP_DEBUG_FILE/tmp/mycli-comp-debug.log从 源码 看CompDebug会带[Debug]前缀追加写入BASH_COMP_DEBUG_FILE指向的文件printToStdErr为true时同时输出到 stderrCompError/CompErrorln带[Error]前缀且总是同时输出到 stderr。调试完成后再跑一次__complete检查该文件里函数的执行轨迹是否符合预期。另外文档指出直接调用__complete还可以让你用 Go 调试器debugger对这段补全代码做断点排查而不必依赖 print 语句。可选去掉描述文本让输出更干净如果你用cobra.CompletionWithDesc之类方式给候选项带了解释文本观察输出时可以临时关闭描述把PROGRAM_COMPLETION_DESCRIPTIONS环境变量未设置时回退到COBRA_COMPLETION_DESCRIPTIONS设为 falsey value例如export COBRA_COMPLETION_DESCRIPTIONSfalse程序名即根命令名大写、所有非 ASCII 字母数字字符替换为_。源码中还提供了一个别名__completeNoDesc效果同样是去掉候选项中 tab 之后的描述部分见 completions.go。可选调试 Active Help 消息如果你的补全函数用cobra.AppendActiveHelp(...)输出提示消息Active Help 文档 说明调试方式同样是__complete并通过PROGRAM_ACTIVE_HELP环境变量切换配置来验证不同行为文档示例HELM_ACTIVE_HELP即程序名helm的大写形式$ HELM_ACTIVE_HELP1 bin/helm __complete install wordpress bitnami/h bitnami/haproxy bitnami/harbor _activeHelp_ WARNING: cannot re-use a name that is still in use :0 Completion ended with directive: ShellCompDirectiveDefault $ HELM_ACTIVE_HELP0 bin/helm __complete install wordpress bitnami/h bitnami/haproxy bitnami/harbor :0 Completion ended with directive: ShellCompDirectiveDefault对比两次输出即可验证 Active Help 消息是否按配置出现或被禁用HELM_ACTIVE_HELP0时_activeHelp_前缀的行不再出现。验证与边界说明成功标准stdout 上的候选项逐行等于你的补全函数返回的列表最后一行:编号与 stderr 提示的指令名一致且等于函数返回的ShellCompDirective文档示例中NoFileComp对应:4。命令形态__complete [command-line]至少需要一个参数且最后一个参数是尚未输入完的片段片段为空时传这是文档明确要求的不是可选项。stdout 纪律补全代码中任何直接输出到 stdout 的内容都会污染候选项列表调试输出一律走cobra.CompDebug*/cobra.CompError*。各 shell 的限制例如 zsh/fish 不支持 legacy bash 脚本补全见 Shell Completions 文档 中各 shell 的 Limitations 小节__complete调试机制本身对这些 shell 通用因为所有补全脚本都通过它请求候选项。【免费下载链接】cobraA Commander for modern Go CLI interactions项目地址: https://gitcode.com/GitHub_Trending/co/cobra创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考