Search code examples
bashbash-completioncommand-line-arguments

bash completion _parse_help broken?


In order to get easy bash completion for my scripts' command line switches, I have been exploring the _parse_help function in /etc/bash_completion (debian sid, bash v4.2-1, bash-completion v1:1.99-3).

My script generates a help message in what I believe to be standard GNU format:

MYSCRIPT [OPTIONS]

Usage:
  -h, --help     Show this help message
  -o, --option   Some option

I then activate completion via complete -F _parse_help ./myscript.

Unfortunately this does not give the desired result. Upon first tab ./myscript <TAB> (note, just a single keypress) an unformatted list appears containing --help and --option, not the shortened versions. Worse, completion of either option fails. In fact, just typing a dash causes _parse_help to completely stop generating output.

The whole thing feels very much broken, and I cannot find much reference of it online. Is this function perhaps considered obsolete? Is there another standard method of command line completion based on parsing --help output?


Solution

  • According to the bash reference manual the -F function expects to read the word list from a variable COMPREPLY

    It would appear that _parse_help does not set this required variable and would therefor not be suited as a function for complete -F. The output is obviously echo'd as is evident from executing the function.

    $ _parse_help ./myscript 
    -h
    --help
    -o
    --option
    

    @gertjan What you were attempting can be accomplished using the -W wordlist option instead.

    $ complete -W "$(_parse_help ./myscript)" ./myscript 
    $ ./myscript -
    --help    --option  -h        -o
    $ ./myscript --
    --help    --option
    $ ./myscript --help
    

    Completion works as expected and --h will complete --help or where there were multiple arguments like with "--" it will only list the appropriate options.

    If we were trying to use a function however it is not sufficient alone to only set COMPREPLY as you will see from the next example.

    NOTE: COMPREPLY is a bash array and requires the brackets () when set

    $ function _myscript () { 
    >     COMPREPLY=($(_parse_help ./myscript))
    > }
    $ complete -F _myscript ./myscript 
    $ ./myscript -
    --help    --option  -h        -o
    $ ./myscript --
    --help    --option  -h        -o
    $ ./myscript --help
    --help    --option  -h        -o  
    

    As you can see, even though the options are displayed, complete now expects us to do the filtering. We can accomplish this with compgen but first we need to determine what the current argument is to filter against. The function _get_comp_words_by_ref can help with this by populating the $cur variable, as per our final example.

    The complete implementation using a function with _parse_help for bash completion.

    $ function _myscript () {
    >     _get_comp_words_by_ref cur
    >     COMPREPLY=($(compgen -W "$(_parse_help ./myscript)" -- "$cur"))
    > }
    $ complete -F _myscript ./myscript 
    $ ./myscript - 
    --help    --option  -h        -o
    $ ./myscript --
    --help    --option
    $ ./myscript --help
    

    nJoy!