Generating Bash Completions For Your Own cobra.Command

如果你正在使用 generator,可以通过运行下面的命令来创建补全命名:

  1. cobra add completion

更新帮助文本,显示 linux 如何安装 bash completion, 查看这里 Kubectl docs show mac options

  1. // completionCmd represents the completion command
  2. var completionCmd = &cobra.Command{
  3. Use: "completion",
  4. Short: "Generates bash completion scripts",
  5. Long: `To load completion run
  6. . <(bitbucket completion)
  7. To configure your bash shell to load completions for each session add to your bashrc
  8. # ~/.bashrc or ~/.profile
  9. . <(bitbucket completion)
  10. `,
  11. Run: func(cmd *cobra.Command, args []string) {
  12. rootCmd.GenBashCompletion(os.Stdout);
  13. },
  14. }

注意: cobra generator 可能包含打印到 stdout 的消息,例如,如果加载了配置文件,这将破坏自动补全脚本

Example from kubectl

从一个 cobra 命令生成 bash completion 非常简单。kubernetes kubectl 二进制文件的实际程序如下:

  1. package main
  2. import (
  3. "io/ioutil"
  4. "os"
  5. "k8s.io/kubernetes/pkg/kubectl/cmd"
  6. "k8s.io/kubernetes/pkg/kubectl/cmd/util"
  7. )
  8. func main() {
  9. kubectl := cmd.NewKubectlCommand(util.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard)
  10. kubectl.GenBashCompletionFile("out.sh")
  11. }

out.sh 会补全子命令和标志。按此处描述, 将它复制到 /etc/bash_completion.d/,并重置你的 terminal。如果在代码中添加额外的注释,可以获得更加智能和灵活的行为。

Creating your own custom functions

一些在 kubernetes 中的实际代码:

  1. const (
  2. bash_completion_func = `__kubectl_parse_get()
  3. {
  4. local kubectl_output out
  5. if kubectl_output=$(kubectl get --no-headers "$1" 2>/dev/null); then
  6. out=($(echo "${kubectl_output}" | awk '{print $1}'))
  7. COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
  8. fi
  9. }
  10. __kubectl_get_resource()
  11. {
  12. if [[ ${#nouns[@]} -eq 0 ]]; then
  13. return 1
  14. fi
  15. __kubectl_parse_get ${nouns[${#nouns[@]} -1]}
  16. if [[ $? -eq 0 ]]; then
  17. return 0
  18. fi
  19. }
  20. __kubectl_custom_func() {
  21. case ${last_command} in
  22. kubectl_get | kubectl_describe | kubectl_delete | kubectl_stop)
  23. __kubectl_get_resource
  24. return
  25. ;;
  26. *)
  27. ;;
  28. esac
  29. }
  30. `)

然后我在命令定义中设置:

  1. cmds := &cobra.Command{
  2. Use: "kubectl",
  3. Short: "kubectl controls the Kubernetes cluster manager",
  4. Long: `kubectl controls the Kubernetes cluster manager.
  5. Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
  6. Run: runHelp,
  7. BashCompletionFunction: bash_completion_func,
  8. }

BashCompletionFunction 选项实际上只在根命令上有效/有用。执行上述操作将导致在内置处理器无法找到解决方案 时调用 __kubectl_custom_func() (__<command-use>_custom_func())。在 kubernetes 中,一个有效的命令可能 类似于 kubectl get pod [mypod]。如果你键入 kubectl get pod [tab][tab]__kubectl_custom_func() 将运行, 因为 cobra.Command 只理解 kubectlget__kubectl_custom_func() 会看到 cobra.Commandkubectl get, 因此将调用另一个 helper __kubectl_get_resource()__kubectl_get_resource 会看到所收集的 “nouns”。 在我们的例子中,唯一的名词是 pod。所以它会调用 __kubectl_parse_get pod__kubectl_parse_get 会 call kubernetes 并 得到 pod。然后它将设置 COMPREPLY 为有效的 pods!

Have the completions code complete your ‘nouns’

在上面的示例中,假定 “pod” 已经被键入。但是,如果你希望 kubectl get [tab][tab] 显示有效的 “nouns” 列表,则必须设置它们。 kubectl get 的简化代码如下:

  1. validArgs []string = { "pod", "node", "service", "replicationcontroller" }
  2. cmd := &cobra.Command{
  3. Use: "get [(-o|--output=)json|yaml|template|...] (RESOURCE [NAME] | RESOURCE/NAME ...)",
  4. Short: "Display one or many resources",
  5. Long: get_long,
  6. Example: get_example,
  7. Run: func(cmd *cobra.Command, args []string) {
  8. err := RunGet(f, out, cmd, args)
  9. util.CheckErr(err)
  10. },
  11. ValidArgs: validArgs,
  12. }

注意,我们将 ValidArgs 放在 get 子命令上。这样做会得到如下结果:

  1. # kubectl get [tab][tab]
  2. node pod replicationcontroller service

Plural form and shortcuts for nouns

如果你的名词有很多别名,你可以在 ValidArgs 旁边使用 ArgAliases 来定义它们:

  1. argAliases []string = { "pods", "nodes", "services", "svc", "replicationcontrollers", "rc" }
  2. cmd := &cobra.Command{
  3. ...
  4. ValidArgs: validArgs,
  5. ArgAliases: argAliases
  6. }

这些别名在 tab 补全中不会显示给用户,但是如果手工输入,补全算法会将它们作为有效名词接受,例如:

  1. # kubectl get rc [tab][tab]
  2. backend frontend database
  3. ``
  4. ### Mark flags as required
  5. 大多数情况下,补全只显示子命令。但是,如果要使一个子命令工作,有一个 flag 是必须的,你可能希望它在用户键入 `[tab][tab]` 时显示出来。
  6. flag 标记为 `Required`
  7. ```go
  8. cmd.MarkFlagRequired("pod")
  9. cmd.MarkFlagRequired("container")

这样做会得到如下结果:

  1. # kubectl exec [tab][tab][tab]
  2. -c --container= -p --pod=

Specify valid filename extensions for flags that take a filename

在本例中,我们使用 --filename= 并期望得到一个 json 或 yaml 文件作为参数。 为了使这更容易,我们使用有效的文件名扩展名来注释 --filename 标志。

  1. annotations := []string{"json", "yaml", "yml"}
  2. annotation := make(map[string][]string)
  3. annotation[cobra.BashCompFilenameExt] = annotations
  4. flag := &pflag.Flag{
  5. Name: "filename",
  6. Shorthand: "f",
  7. Usage: usage,
  8. Value: value,
  9. DefValue: value.String(),
  10. Annotations: annotation,
  11. }
  12. cmd.Flags().AddFlag(flag)

现在,当运行带有 --filename-f 的命令时,将得到像下面结果:

  1. # kubectl create -f
  2. test/ example/ rpmbuild/
  3. hello.yml test.json

因此,虽然 CWD 中还有许多其他文件,但它只显示子目录和具有有效扩展名的文件。

Specify custom flag completion

类似于使用 cobra.BashCompFilenameExt 来补全和过滤文件名。你可以使用 cobra.BashCompCustom 指定一个自定义标志补全函数:

  1. annotation := make(map[string][]string)
  2. annotation[cobra.BashCompCustom] = []string{"__kubectl_get_namespaces"}
  3. flag := &pflag.Flag{
  4. Name: "namespace",
  5. Usage: usage,
  6. Annotations: annotation,
  7. }
  8. cmd.Flags().AddFlag(flag)

此外,在 BashCompletionFunction 的值中添加 __handle_namespace_flag 实现,例如:

  1. __kubectl_get_namespaces()
  2. {
  3. local template
  4. template="{{ range .items }}{{ .metadata.name }} {{ end }}"
  5. local kubectl_out
  6. if kubectl_out=$(kubectl get -o template --template="${template}" namespace 2>/dev/null); then
  7. COMPREPLY=( $( compgen -W "${kubectl_out}[*]" -- "$cur" ) )
  8. fi
  9. }

Using bash aliases for commands

还可以为命令配置 bash aliases,并仍会支持补全。

  1. alias aliasname=origcommand
  2. complete -o default -F __start_origcommand aliasname
  3. # and now when you run `aliasname` completion will make
  4. # suggestions as it did for `origcommand`.
  5. $) aliasname <tab><tab>
  6. completion firstcommand secondcommand