Nested bash autocompletion script

懵懂的女人 提交于 2021-02-07 10:36:03

问题


I've been trying to add bash completion support to a command line program I've been using lately, but it seems that I've hit a wall.

Here are the commands and subcommands I'd like to auto-complete

  • Main command is foo, autocompletion should be performed for the subcommands version, process, and help. Further autcompletion depends on the subcommand.

    • If the user enters - after the main command, autocompletion performs completion for these word: --activate or --deactivate

    • If the user enters --deactivate, it should perform autocompletion of a bash command output (let's say ls).

  • version: no autocompletion performed
  • process: autocompletion defaults to listing directories (current and parent included)

    • If the user enters -, autocompletion performs completion for these words: --color, --verbose and then stops afterward
  • If the user enters help, autocompletion is for the other subcommands (process or version).

Here's my current implementation of the autocomplete script (which is failing badly):

_foo() {
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    words=("${COMP_WORDS[@]}")
    cword=$COMP_WORD
    opts="process version help"

    case "$prev" in
    -*)
        local subopts="--activate --deactivate"
            COMPREPLY=( $(compgen -W "${subopts}" -- ${cur}) )
            case "$cur" in
                --deactivate)
                    COMPREPLY=( $(ls) )
                ;;
            esac
         ;;
    version)
        COMPREPLY=()
        ;;
    process)
        case "$cur" in
            -*)
                local subopts="--color --verbose"
                COMPREPLY=( $(compgen -W "${subopts}" -- ${cur}) )
                ;;
            *)
                COMPREPLY=( $(compgen -A directory))
                ;;
        esac
        ;;
    help)
        COMPREPLY=( $(compgen -W "process version" -- ${cur}) )
        ;;
   esac
   } 
 complete -F _foo foo

You can probably see that Bash is not my main forte as well. I was thinking of writing separate bash functions for each subcommands, but I don't know how to do it at the moment. If you have suggestions on how to implement this as well, I would really appreciate it.

Thanks!


回答1:


In case of the possible subcommands on first level it's a bit unclear for me. Should it be possible to enter foo --activate or foo - --activate? The latter looks somehow strange but fits more likely to your given code. The first sounds more reasonable, but would imply to have --activate etc. as kind of global parameter that is to be treated on the same level like your subcommands.

Nevertheless you missed the default, when you haven't entered anything yet. Try the following code:

_foo() {
    local cur prev opts

    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    words=("${COMP_WORDS[@]}")
    cword=$COMP_CWORD
    opts="process version help"

    case "$prev" in
    -*)
        local subopts="--activate --deactivate"
        COMPREPLY=( $(compgen -W "${subopts}" -- ${cur}) )
        case "$cur" in
            --deactivate)
                COMPREPLY=( $(ls) )
            ;;
        esac
        return 0
         ;;
    version)
        COMPREPLY=()
        return 0
        ;;
    process)
        case "$cur" in
            -*)
                local subopts="--color --verbose"
                COMPREPLY=( $(compgen -W "${subopts}" -- ${cur}) )
                ;;
            *)
                COMPREPLY=( $(compgen -A directory))
                ;;
        esac
        return 0
        ;;
    help)
        COMPREPLY=( $(compgen -W "process version" -- ${cur}) )
        return 0
        ;;
   esac
   COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
   return 0
} 
complete -F _foo foo

which is basically the same as yours but: At the bottom, set COMPREPLY to your first level subcommands so they get completed. In case of having entered a subcommand, you should return (0) to not reach the final statement.

The example given will work with foo - --activate which may not what you wanted. So if you wanted to enter foo --activate change the according lines to

    case "$prev" in
    --activate)
        COMPREPLY=()
        return 0
        ;;
    --deactivate)
        COMPREPLY=( $(compgen -W "$(ls)" -- ${cur}) )
        return 0
        ;;
    version)
    ...


来源:https://stackoverflow.com/questions/9340045/nested-bash-autocompletion-script

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!