Path: blob/main/vendor/github.com/spf13/cobra/bash_completions.go
2875 views
// Copyright 2013-2023 The Cobra Authors1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314package cobra1516import (17"bytes"18"fmt"19"io"20"os"21"sort"22"strings"2324"github.com/spf13/pflag"25)2627// Annotations for Bash completion.28const (29BashCompFilenameExt = "cobra_annotation_bash_completion_filename_extensions"30BashCompCustom = "cobra_annotation_bash_completion_custom"31BashCompOneRequiredFlag = "cobra_annotation_bash_completion_one_required_flag"32BashCompSubdirsInDir = "cobra_annotation_bash_completion_subdirs_in_dir"33)3435func writePreamble(buf io.StringWriter, name string) {36WriteStringAndCheck(buf, fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name))37WriteStringAndCheck(buf, fmt.Sprintf(`38__%[1]s_debug()39{40if [[ -n ${BASH_COMP_DEBUG_FILE:-} ]]; then41echo "$*" >> "${BASH_COMP_DEBUG_FILE}"42fi43}4445# Homebrew on Macs have version 1.3 of bash-completion which doesn't include46# _init_completion. This is a very minimal version of that function.47__%[1]s_init_completion()48{49COMPREPLY=()50_get_comp_words_by_ref "$@" cur prev words cword51}5253__%[1]s_index_of_word()54{55local w word=$156shift57index=058for w in "$@"; do59[[ $w = "$word" ]] && return60index=$((index+1))61done62index=-163}6465__%[1]s_contains_word()66{67local w word=$1; shift68for w in "$@"; do69[[ $w = "$word" ]] && return70done71return 172}7374__%[1]s_handle_go_custom_completion()75{76__%[1]s_debug "${FUNCNAME[0]}: cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}"7778local shellCompDirectiveError=%[3]d79local shellCompDirectiveNoSpace=%[4]d80local shellCompDirectiveNoFileComp=%[5]d81local shellCompDirectiveFilterFileExt=%[6]d82local shellCompDirectiveFilterDirs=%[7]d8384local out requestComp lastParam lastChar comp directive args8586# Prepare the command to request completions for the program.87# Calling ${words[0]} instead of directly %[1]s allows handling aliases88args=("${words[@]:1}")89# Disable ActiveHelp which is not supported for bash completion v190requestComp="%[8]s=0 ${words[0]} %[2]s ${args[*]}"9192lastParam=${words[$((${#words[@]}-1))]}93lastChar=${lastParam:$((${#lastParam}-1)):1}94__%[1]s_debug "${FUNCNAME[0]}: lastParam ${lastParam}, lastChar ${lastChar}"9596if [ -z "${cur}" ] && [ "${lastChar}" != "=" ]; then97# If the last parameter is complete (there is a space following it)98# We add an extra empty parameter so we can indicate this to the go method.99__%[1]s_debug "${FUNCNAME[0]}: Adding extra empty parameter"100requestComp="${requestComp} \"\""101fi102103__%[1]s_debug "${FUNCNAME[0]}: calling ${requestComp}"104# Use eval to handle any environment variables and such105out=$(eval "${requestComp}" 2>/dev/null)106107# Extract the directive integer at the very end of the output following a colon (:)108directive=${out##*:}109# Remove the directive110out=${out%%:*}111if [ "${directive}" = "${out}" ]; then112# There is not directive specified113directive=0114fi115__%[1]s_debug "${FUNCNAME[0]}: the completion directive is: ${directive}"116__%[1]s_debug "${FUNCNAME[0]}: the completions are: ${out}"117118if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then119# Error code. No completion.120__%[1]s_debug "${FUNCNAME[0]}: received error from custom completion go code"121return122else123if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then124if [[ $(type -t compopt) = "builtin" ]]; then125__%[1]s_debug "${FUNCNAME[0]}: activating no space"126compopt -o nospace127fi128fi129if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then130if [[ $(type -t compopt) = "builtin" ]]; then131__%[1]s_debug "${FUNCNAME[0]}: activating no file completion"132compopt +o default133fi134fi135fi136137if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then138# File extension filtering139local fullFilter filter filteringCmd140# Do not use quotes around the $out variable or else newline141# characters will be kept.142for filter in ${out}; do143fullFilter+="$filter|"144done145146filteringCmd="_filedir $fullFilter"147__%[1]s_debug "File filtering command: $filteringCmd"148$filteringCmd149elif [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then150# File completion for directories only151local subdir152# Use printf to strip any trailing newline153subdir=$(printf "%%s" "${out}")154if [ -n "$subdir" ]; then155__%[1]s_debug "Listing directories in $subdir"156__%[1]s_handle_subdirs_in_dir_flag "$subdir"157else158__%[1]s_debug "Listing directories in ."159_filedir -d160fi161else162while IFS='' read -r comp; do163COMPREPLY+=("$comp")164done < <(compgen -W "${out}" -- "$cur")165fi166}167168__%[1]s_handle_reply()169{170__%[1]s_debug "${FUNCNAME[0]}"171local comp172case $cur in173-*)174if [[ $(type -t compopt) = "builtin" ]]; then175compopt -o nospace176fi177local allflags178if [ ${#must_have_one_flag[@]} -ne 0 ]; then179allflags=("${must_have_one_flag[@]}")180else181allflags=("${flags[*]} ${two_word_flags[*]}")182fi183while IFS='' read -r comp; do184COMPREPLY+=("$comp")185done < <(compgen -W "${allflags[*]}" -- "$cur")186if [[ $(type -t compopt) = "builtin" ]]; then187[[ "${COMPREPLY[0]}" == *= ]] || compopt +o nospace188fi189190# complete after --flag=abc191if [[ $cur == *=* ]]; then192if [[ $(type -t compopt) = "builtin" ]]; then193compopt +o nospace194fi195196local index flag197flag="${cur%%=*}"198__%[1]s_index_of_word "${flag}" "${flags_with_completion[@]}"199COMPREPLY=()200if [[ ${index} -ge 0 ]]; then201PREFIX=""202cur="${cur#*=}"203${flags_completion[${index}]}204if [ -n "${ZSH_VERSION:-}" ]; then205# zsh completion needs --flag= prefix206eval "COMPREPLY=( \"\${COMPREPLY[@]/#/${flag}=}\" )"207fi208fi209fi210211if [[ -z "${flag_parsing_disabled}" ]]; then212# If flag parsing is enabled, we have completed the flags and can return.213# If flag parsing is disabled, we may not know all (or any) of the flags, so we fallthrough214# to possibly call handle_go_custom_completion.215return 0;216fi217;;218esac219220# check if we are handling a flag with special work handling221local index222__%[1]s_index_of_word "${prev}" "${flags_with_completion[@]}"223if [[ ${index} -ge 0 ]]; then224${flags_completion[${index}]}225return226fi227228# we are parsing a flag and don't have a special handler, no completion229if [[ ${cur} != "${words[cword]}" ]]; then230return231fi232233local completions234completions=("${commands[@]}")235if [[ ${#must_have_one_noun[@]} -ne 0 ]]; then236completions+=("${must_have_one_noun[@]}")237elif [[ -n "${has_completion_function}" ]]; then238# if a go completion function is provided, defer to that function239__%[1]s_handle_go_custom_completion240fi241if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then242completions+=("${must_have_one_flag[@]}")243fi244while IFS='' read -r comp; do245COMPREPLY+=("$comp")246done < <(compgen -W "${completions[*]}" -- "$cur")247248if [[ ${#COMPREPLY[@]} -eq 0 && ${#noun_aliases[@]} -gt 0 && ${#must_have_one_noun[@]} -ne 0 ]]; then249while IFS='' read -r comp; do250COMPREPLY+=("$comp")251done < <(compgen -W "${noun_aliases[*]}" -- "$cur")252fi253254if [[ ${#COMPREPLY[@]} -eq 0 ]]; then255if declare -F __%[1]s_custom_func >/dev/null; then256# try command name qualified custom func257__%[1]s_custom_func258else259# otherwise fall back to unqualified for compatibility260declare -F __custom_func >/dev/null && __custom_func261fi262fi263264# available in bash-completion >= 2, not always present on macOS265if declare -F __ltrim_colon_completions >/dev/null; then266__ltrim_colon_completions "$cur"267fi268269# If there is only 1 completion and it is a flag with an = it will be completed270# but we don't want a space after the =271if [[ "${#COMPREPLY[@]}" -eq "1" ]] && [[ $(type -t compopt) = "builtin" ]] && [[ "${COMPREPLY[0]}" == --*= ]]; then272compopt -o nospace273fi274}275276# The arguments should be in the form "ext1|ext2|extn"277__%[1]s_handle_filename_extension_flag()278{279local ext="$1"280_filedir "@(${ext})"281}282283__%[1]s_handle_subdirs_in_dir_flag()284{285local dir="$1"286pushd "${dir}" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 || return287}288289__%[1]s_handle_flag()290{291__%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"292293# if a command required a flag, and we found it, unset must_have_one_flag()294local flagname=${words[c]}295local flagvalue=""296# if the word contained an =297if [[ ${words[c]} == *"="* ]]; then298flagvalue=${flagname#*=} # take in as flagvalue after the =299flagname=${flagname%%=*} # strip everything after the =300flagname="${flagname}=" # but put the = back301fi302__%[1]s_debug "${FUNCNAME[0]}: looking for ${flagname}"303if __%[1]s_contains_word "${flagname}" "${must_have_one_flag[@]}"; then304must_have_one_flag=()305fi306307# if you set a flag which only applies to this command, don't show subcommands308if __%[1]s_contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then309commands=()310fi311312# keep flag value with flagname as flaghash313# flaghash variable is an associative array which is only supported in bash > 3.314if [[ -z "${BASH_VERSION:-}" || "${BASH_VERSINFO[0]:-}" -gt 3 ]]; then315if [ -n "${flagvalue}" ] ; then316flaghash[${flagname}]=${flagvalue}317elif [ -n "${words[ $((c+1)) ]}" ] ; then318flaghash[${flagname}]=${words[ $((c+1)) ]}319else320flaghash[${flagname}]="true" # pad "true" for bool flag321fi322fi323324# skip the argument to a two word flag325if [[ ${words[c]} != *"="* ]] && __%[1]s_contains_word "${words[c]}" "${two_word_flags[@]}"; then326__%[1]s_debug "${FUNCNAME[0]}: found a flag ${words[c]}, skip the next argument"327c=$((c+1))328# if we are looking for a flags value, don't show commands329if [[ $c -eq $cword ]]; then330commands=()331fi332fi333334c=$((c+1))335336}337338__%[1]s_handle_noun()339{340__%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"341342if __%[1]s_contains_word "${words[c]}" "${must_have_one_noun[@]}"; then343must_have_one_noun=()344elif __%[1]s_contains_word "${words[c]}" "${noun_aliases[@]}"; then345must_have_one_noun=()346fi347348nouns+=("${words[c]}")349c=$((c+1))350}351352__%[1]s_handle_command()353{354__%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"355356local next_command357if [[ -n ${last_command} ]]; then358next_command="_${last_command}_${words[c]//:/__}"359else360if [[ $c -eq 0 ]]; then361next_command="_%[1]s_root_command"362else363next_command="_${words[c]//:/__}"364fi365fi366c=$((c+1))367__%[1]s_debug "${FUNCNAME[0]}: looking for ${next_command}"368declare -F "$next_command" >/dev/null && $next_command369}370371__%[1]s_handle_word()372{373if [[ $c -ge $cword ]]; then374__%[1]s_handle_reply375return376fi377__%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"378if [[ "${words[c]}" == -* ]]; then379__%[1]s_handle_flag380elif __%[1]s_contains_word "${words[c]}" "${commands[@]}"; then381__%[1]s_handle_command382elif [[ $c -eq 0 ]]; then383__%[1]s_handle_command384elif __%[1]s_contains_word "${words[c]}" "${command_aliases[@]}"; then385# aliashash variable is an associative array which is only supported in bash > 3.386if [[ -z "${BASH_VERSION:-}" || "${BASH_VERSINFO[0]:-}" -gt 3 ]]; then387words[c]=${aliashash[${words[c]}]}388__%[1]s_handle_command389else390__%[1]s_handle_noun391fi392else393__%[1]s_handle_noun394fi395__%[1]s_handle_word396}397398`, name, ShellCompNoDescRequestCmd,399ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,400ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, activeHelpEnvVar(name)))401}402403func writePostscript(buf io.StringWriter, name string) {404name = strings.ReplaceAll(name, ":", "__")405WriteStringAndCheck(buf, fmt.Sprintf("__start_%s()\n", name))406WriteStringAndCheck(buf, fmt.Sprintf(`{407local cur prev words cword split408declare -A flaghash 2>/dev/null || :409declare -A aliashash 2>/dev/null || :410if declare -F _init_completion >/dev/null 2>&1; then411_init_completion -s || return412else413__%[1]s_init_completion -n "=" || return414fi415416local c=0417local flag_parsing_disabled=418local flags=()419local two_word_flags=()420local local_nonpersistent_flags=()421local flags_with_completion=()422local flags_completion=()423local commands=("%[1]s")424local command_aliases=()425local must_have_one_flag=()426local must_have_one_noun=()427local has_completion_function=""428local last_command=""429local nouns=()430local noun_aliases=()431432__%[1]s_handle_word433}434435`, name))436WriteStringAndCheck(buf, fmt.Sprintf(`if [[ $(type -t compopt) = "builtin" ]]; then437complete -o default -F __start_%s %s438else439complete -o default -o nospace -F __start_%s %s440fi441442`, name, name, name, name))443WriteStringAndCheck(buf, "# ex: ts=4 sw=4 et filetype=sh\n")444}445446func writeCommands(buf io.StringWriter, cmd *Command) {447WriteStringAndCheck(buf, " commands=()\n")448for _, c := range cmd.Commands() {449if !c.IsAvailableCommand() && c != cmd.helpCommand {450continue451}452WriteStringAndCheck(buf, fmt.Sprintf(" commands+=(%q)\n", c.Name()))453writeCmdAliases(buf, c)454}455WriteStringAndCheck(buf, "\n")456}457458func writeFlagHandler(buf io.StringWriter, name string, annotations map[string][]string, cmd *Command) {459for key, value := range annotations {460switch key {461case BashCompFilenameExt:462WriteStringAndCheck(buf, fmt.Sprintf(" flags_with_completion+=(%q)\n", name))463464var ext string465if len(value) > 0 {466ext = fmt.Sprintf("__%s_handle_filename_extension_flag ", cmd.Root().Name()) + strings.Join(value, "|")467} else {468ext = "_filedir"469}470WriteStringAndCheck(buf, fmt.Sprintf(" flags_completion+=(%q)\n", ext))471case BashCompCustom:472WriteStringAndCheck(buf, fmt.Sprintf(" flags_with_completion+=(%q)\n", name))473474if len(value) > 0 {475handlers := strings.Join(value, "; ")476WriteStringAndCheck(buf, fmt.Sprintf(" flags_completion+=(%q)\n", handlers))477} else {478WriteStringAndCheck(buf, " flags_completion+=(:)\n")479}480case BashCompSubdirsInDir:481WriteStringAndCheck(buf, fmt.Sprintf(" flags_with_completion+=(%q)\n", name))482483var ext string484if len(value) == 1 {485ext = fmt.Sprintf("__%s_handle_subdirs_in_dir_flag ", cmd.Root().Name()) + value[0]486} else {487ext = "_filedir -d"488}489WriteStringAndCheck(buf, fmt.Sprintf(" flags_completion+=(%q)\n", ext))490}491}492}493494const cbn = "\")\n"495496func writeShortFlag(buf io.StringWriter, flag *pflag.Flag, cmd *Command) {497name := flag.Shorthand498format := " "499if len(flag.NoOptDefVal) == 0 {500format += "two_word_"501}502format += "flags+=(\"-%s" + cbn503WriteStringAndCheck(buf, fmt.Sprintf(format, name))504writeFlagHandler(buf, "-"+name, flag.Annotations, cmd)505}506507func writeFlag(buf io.StringWriter, flag *pflag.Flag, cmd *Command) {508name := flag.Name509format := " flags+=(\"--%s"510if len(flag.NoOptDefVal) == 0 {511format += "="512}513format += cbn514WriteStringAndCheck(buf, fmt.Sprintf(format, name))515if len(flag.NoOptDefVal) == 0 {516format = " two_word_flags+=(\"--%s" + cbn517WriteStringAndCheck(buf, fmt.Sprintf(format, name))518}519writeFlagHandler(buf, "--"+name, flag.Annotations, cmd)520}521522func writeLocalNonPersistentFlag(buf io.StringWriter, flag *pflag.Flag) {523name := flag.Name524format := " local_nonpersistent_flags+=(\"--%[1]s" + cbn525if len(flag.NoOptDefVal) == 0 {526format += " local_nonpersistent_flags+=(\"--%[1]s=" + cbn527}528WriteStringAndCheck(buf, fmt.Sprintf(format, name))529if len(flag.Shorthand) > 0 {530WriteStringAndCheck(buf, fmt.Sprintf(" local_nonpersistent_flags+=(\"-%s\")\n", flag.Shorthand))531}532}533534// prepareCustomAnnotationsForFlags setup annotations for go completions for registered flags535func prepareCustomAnnotationsForFlags(cmd *Command) {536flagCompletionMutex.RLock()537defer flagCompletionMutex.RUnlock()538for flag := range flagCompletionFunctions {539// Make sure the completion script calls the __*_go_custom_completion function for540// every registered flag. We need to do this here (and not when the flag was registered541// for completion) so that we can know the root command name for the prefix542// of __<prefix>_go_custom_completion543if flag.Annotations == nil {544flag.Annotations = map[string][]string{}545}546flag.Annotations[BashCompCustom] = []string{fmt.Sprintf("__%[1]s_handle_go_custom_completion", cmd.Root().Name())}547}548}549550func writeFlags(buf io.StringWriter, cmd *Command) {551prepareCustomAnnotationsForFlags(cmd)552WriteStringAndCheck(buf, ` flags=()553two_word_flags=()554local_nonpersistent_flags=()555flags_with_completion=()556flags_completion=()557558`)559560if cmd.DisableFlagParsing {561WriteStringAndCheck(buf, " flag_parsing_disabled=1\n")562}563564localNonPersistentFlags := cmd.LocalNonPersistentFlags()565cmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) {566if nonCompletableFlag(flag) {567return568}569writeFlag(buf, flag, cmd)570if len(flag.Shorthand) > 0 {571writeShortFlag(buf, flag, cmd)572}573// localNonPersistentFlags are used to stop the completion of subcommands when one is set574// if TraverseChildren is true we should allow to complete subcommands575if localNonPersistentFlags.Lookup(flag.Name) != nil && !cmd.Root().TraverseChildren {576writeLocalNonPersistentFlag(buf, flag)577}578})579cmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) {580if nonCompletableFlag(flag) {581return582}583writeFlag(buf, flag, cmd)584if len(flag.Shorthand) > 0 {585writeShortFlag(buf, flag, cmd)586}587})588589WriteStringAndCheck(buf, "\n")590}591592func writeRequiredFlag(buf io.StringWriter, cmd *Command) {593WriteStringAndCheck(buf, " must_have_one_flag=()\n")594flags := cmd.NonInheritedFlags()595flags.VisitAll(func(flag *pflag.Flag) {596if nonCompletableFlag(flag) {597return598}599if _, ok := flag.Annotations[BashCompOneRequiredFlag]; ok {600format := " must_have_one_flag+=(\"--%s"601if flag.Value.Type() != "bool" {602format += "="603}604format += cbn605WriteStringAndCheck(buf, fmt.Sprintf(format, flag.Name))606607if len(flag.Shorthand) > 0 {608WriteStringAndCheck(buf, fmt.Sprintf(" must_have_one_flag+=(\"-%s"+cbn, flag.Shorthand))609}610}611})612}613614func writeRequiredNouns(buf io.StringWriter, cmd *Command) {615WriteStringAndCheck(buf, " must_have_one_noun=()\n")616sort.Strings(cmd.ValidArgs)617for _, value := range cmd.ValidArgs {618// Remove any description that may be included following a tab character.619// Descriptions are not supported by bash completion.620value = strings.SplitN(value, "\t", 2)[0]621WriteStringAndCheck(buf, fmt.Sprintf(" must_have_one_noun+=(%q)\n", value))622}623if cmd.ValidArgsFunction != nil {624WriteStringAndCheck(buf, " has_completion_function=1\n")625}626}627628func writeCmdAliases(buf io.StringWriter, cmd *Command) {629if len(cmd.Aliases) == 0 {630return631}632633sort.Strings(cmd.Aliases)634635WriteStringAndCheck(buf, fmt.Sprint(` if [[ -z "${BASH_VERSION:-}" || "${BASH_VERSINFO[0]:-}" -gt 3 ]]; then`, "\n"))636for _, value := range cmd.Aliases {637WriteStringAndCheck(buf, fmt.Sprintf(" command_aliases+=(%q)\n", value))638WriteStringAndCheck(buf, fmt.Sprintf(" aliashash[%q]=%q\n", value, cmd.Name()))639}640WriteStringAndCheck(buf, ` fi`)641WriteStringAndCheck(buf, "\n")642}643func writeArgAliases(buf io.StringWriter, cmd *Command) {644WriteStringAndCheck(buf, " noun_aliases=()\n")645sort.Strings(cmd.ArgAliases)646for _, value := range cmd.ArgAliases {647WriteStringAndCheck(buf, fmt.Sprintf(" noun_aliases+=(%q)\n", value))648}649}650651func gen(buf io.StringWriter, cmd *Command) {652for _, c := range cmd.Commands() {653if !c.IsAvailableCommand() && c != cmd.helpCommand {654continue655}656gen(buf, c)657}658commandName := cmd.CommandPath()659commandName = strings.ReplaceAll(commandName, " ", "_")660commandName = strings.ReplaceAll(commandName, ":", "__")661662if cmd.Root() == cmd {663WriteStringAndCheck(buf, fmt.Sprintf("_%s_root_command()\n{\n", commandName))664} else {665WriteStringAndCheck(buf, fmt.Sprintf("_%s()\n{\n", commandName))666}667668WriteStringAndCheck(buf, fmt.Sprintf(" last_command=%q\n", commandName))669WriteStringAndCheck(buf, "\n")670WriteStringAndCheck(buf, " command_aliases=()\n")671WriteStringAndCheck(buf, "\n")672673writeCommands(buf, cmd)674writeFlags(buf, cmd)675writeRequiredFlag(buf, cmd)676writeRequiredNouns(buf, cmd)677writeArgAliases(buf, cmd)678WriteStringAndCheck(buf, "}\n\n")679}680681// GenBashCompletion generates bash completion file and writes to the passed writer.682func (c *Command) GenBashCompletion(w io.Writer) error {683buf := new(bytes.Buffer)684writePreamble(buf, c.Name())685if len(c.BashCompletionFunction) > 0 {686buf.WriteString(c.BashCompletionFunction + "\n")687}688gen(buf, c)689writePostscript(buf, c.Name())690691_, err := buf.WriteTo(w)692return err693}694695func nonCompletableFlag(flag *pflag.Flag) bool {696return flag.Hidden || len(flag.Deprecated) > 0697}698699// GenBashCompletionFile generates bash completion file.700func (c *Command) GenBashCompletionFile(filename string) error {701outFile, err := os.Create(filename)702if err != nil {703return err704}705defer outFile.Close()706707return c.GenBashCompletion(outFile)708}709710711