Bash Completion Scripts (V1 & V2)
Cobra generates Bash completion scripts that integrate shell completion with the command tree and the program’s hidden completion request command. The generated script is responsible for collecting the current command line, invoking the binary, and interpreting its completion output.
Cobra keeps two Bash generators because the legacy generator supports older custom Bash hooks, while the V2 generator is the default path used by Cobra’s built-in completion command.
Sources: completions.go:28-35, site/content/completions/bash.md:5-11
Core concepts
Legacy Bash completion
The legacy generator emits a Bash script that can include custom functions supplied through BashCompletionFunction; those functions provide fallback completion choices for Bash only.
Sources: site/content/completions/bash.md:5-11, command.go:100-102
Bash V2 completion
The V2 generator emits a script that requests completion results from the program and can select either normal results or results without descriptions.
Sources: bash_completionsV2.go:24-29, bash_completionsV2.go:31-467, completions.go:28-35
Completion request command
The generated script calls the hidden __complete command, or __completeNoDesc when descriptions are disabled, to obtain completion results from the Go program.
Sources: completions.go:28-35, bash_completionsV2.go:31-467
Completion annotations
Legacy flag-specific behavior is represented by annotation constants that associate flags with filename, custom-function, required-flag, or directory completion behavior.
| Constant | Plain-language meaning |
|---|---|
BashCompFilenameExt | Associates a flag with filename-extension completion. |
BashCompCustom | Associates a flag with a custom Bash completion function. |
BashCompOneRequiredFlag | Marks a flag as part of a one-required-flag group. |
BashCompSubdirsInDir | Associates a flag with directory completion inside a directory. |
These annotations are consumed while the legacy generator writes flag metadata and handlers into the script.
Sources: bash_completions.go:459-493, bash_completions.go:551-591, shell_completions.go:67-79
How the two generators differ
The legacy entry point is GenBashCompletion. It creates a buffer, writes the legacy preamble, appends the command’s BashCompletionFunction when present, recursively generates command handlers, writes the postscript, and copies the result to the supplied writer.
The V2 entry point is GenBashCompletionV2. It delegates to genBashCompletion, which creates a buffer, calls genBashComp with the command name and the includeDesc choice, then writes the buffer to the supplied writer.
The practical difference is therefore the extension point and request mode: legacy generation can inject custom Bash functions and explicitly disables ActiveHelp in its Go completion request, whereas V2 chooses between __complete and __completeNoDesc based on includeDesc.
The legacy script’s request path builds a command from the current words, adds an empty argument when the cursor follows a completed parameter, evaluates that command, and separates completion output from the final directive integer. The V2 script follows the same broad request shape: it builds args, forms requestComp from the executable, request command, and arguments, evaluates it, and extracts the directive after the final colon.
The generated legacy handlers also encode available commands, flags, required flags, required nouns, command aliases, and argument aliases. Hidden or deprecated flags are excluded from completion generation by nonCompletableFlag.
Sources: bash_completions.go:683-694, bash_completions.go:36-402, bash_completions.go:652-680, bash_completions.go:404-445, bash_completionsV2.go:482-484, bash_completionsV2.go:24-29, bash_completionsV2.go:31-467, bash_completions.go:447-457, bash_completions.go:551-591, bash_completions.go:593-613, bash_completions.go:615-627, bash_completions.go:629-643, bash_completions.go:644-650, bash_completions.go:696-698
How a keypress reaches the Go completion command
When Bash invokes the generated completion function, the script initializes completion variables and then calls its word handler. The legacy handler constructs requestComp from the current words, uses the program name from words[0] so aliases work, and invokes the request with eval. The V2 handler likewise constructs a request using the current words and invokes it with eval.
The request command is __complete for normal output and __completeNoDesc for output without descriptions. The Go-side completion command then supplies completion lines and a directive encoded at the end; the Bash script removes the directive from the output before processing the remaining completions.
This is the generator call order for the legacy stdout path.
Evidence
- callerbash_completions.go:683
- generatorbash_completions.go:683
- preamblebash_completions.go:683
- commandsbash_completions.go:652
- postscriptbash_completions.go:683
- writerbash_completions.go:683
The V2 generator selects its request command before writing the script: includeDesc keeps ShellCompRequestCmd, while false selects ShellCompNoDescRequestCmd. This choice is the main V2 output-mode branch.
Evidence
- v2-generatorbash_completionsV2.go:31
- with-descriptionsbash_completionsV2.go:31
- with-descriptionscompletions.go:28
- without-descriptionsbash_completionsV2.go:31
- without-descriptionscompletions.go:28
- bash-scriptbash_completionsV2.go:31
Sources: bash_completions.go:404-445, bash_completions.go:36-402, bash_completionsV2.go:31-467, completions.go:28-35, site/content/completions/_index.md:240-247
Writing a script to stdout or a file
Use GenBashCompletion when the destination is already an io.Writer, such as os.Stdout; it writes the generated legacy script to that writer and returns any write error. A custom completion command commonly calls the root command’s GenBashCompletion(os.Stdout) for the Bash case.
Use GenBashCompletionV2 when writing the V2 script to an io.Writer; pass true to include descriptions or false to request the no-description mode.
Use GenBashCompletionFile when the legacy script should be written to a filename. It creates the file, defers closing it, and delegates generation to GenBashCompletion. Use GenBashCompletionFileV2 for the equivalent V2 operation; it creates and closes the file, then delegates to GenBashCompletionV2.
The generated command documentation shows both common stdout and redirection patterns: sourcing process output for the current session, or redirecting completion output into a Bash completion directory for later sessions.
Sources: bash_completions.go:683-694, site/content/completions/_index.md:76-86, bash_completionsV2.go:482-484, bash_completions.go:701-709, bash_completionsV2.go:470-478, site/content/completions/_index.md:30-45
How it connects
The generated Bash script sits above Cobra’s dynamic completion engine: it invokes __complete, while command-specific completion logic is supplied through mechanisms such as ValidArgsFunction and RegisterFlagCompletionFunc.
Legacy custom Bash behavior connects through BashCompletionFunction and BashCompCustom; this path is Bash-specific, while ValidArgsFunction is the recommended cross-shell approach.
For the broader command model, see Cobra Overview and Command Tree & Registration The shared completion engine is described in Dynamic Completion Engine Other shell generators use the same completion system, as covered by Zsh, Fish & PowerShell Completions.
Sources: site/content/completions/_index.md:168-189, completions.go:28-38, site/content/completions/bash.md:5-11, shell_completions.go:71-79
Key takeaways
- Legacy generation is
GenBashCompletion; V2 generation isGenBashCompletionV2. - Legacy supports injected
BashCompletionFunctioncode and Bash-specific flag handlers. - Both generated scripts call the binary’s hidden completion request command and parse completion directives.
- Use the
GenBashCompletion*writer methods for stdout and theGenBashCompletionFile*methods for filenames. - V2 selects
__completeor__completeNoDescthroughincludeDesc.