18

This question already has an answer here:

I'd like to be able to get my hands on an array of possible completions for a given partial command. For example, the partial command service um has the following possible completions:

$ service um<TAB><TAB>
umountfs      umountnfs.sh  umountroot

I'd like to have a function completions with the following behavior:

$ for x in $(completions 'service um'); do
> echo $x
> done
umountfs
umountnfs.sh
umountroot

Partial progress: what I've learned so far

Here is an approach which I think can be made into a full answer. I'd definitely like to see that full answer, but given that the relatively simple <TAB><TAB> gives the same functionality in a non-programmatic way, it feels like there could also be a slicker solution.

I can find out that the completion mechanism for the service command is the _service function:

$ complete -p service
complete -F _service service

When this completion function _service is called, a bunch of environment variables are set (namely COMP_{LINE,POINT,KEY,TYPE,WORDS,CWORD}; see the bash man page), the function is given as arguments the command being completed, word being completed, and previous word, and it populates COMPREPLY with the possible completions. So my desired completions function could be defined something like this:

function completions() {
    # Produce an array of tokens in the input.
    read -a words <<< $1

    # Use "complete -p ${words[0]}" to determine how the 
    # completions are computed. This could be complicated
    # if complete is given flags other than -F.
    completion_func=???

    # Set all those COMP_* environment variables appropriately.

    # Run the function to populate COMPREPLY. This version
    # assumes words has length at least 2, but that can be
    # fixed.
    $completion_func ${words[0]} ${words[-1]} ${words[-2]}

    echo ${COMPREPLY[@]}
}

Aside from the complexity relative over <TAB><TAB>, a drawback of this approach is that it changes the environment.

marked as duplicate by Gilles bash Feb 9 '15 at 20:26

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

5

Here's a rudimentary function that I think can serve as starting point. It can fail in several ways, hopefully others here can improve:

completions () (
    if [ -f /usr/share/bash-completion/bash_completion ]; then
        . /usr/share/bash-completion/bash_completion
    elif [ -f /etc/bash_completion ]; then
        . /etc/bash_completion
    fi

    IFS="$COMP_WORDBREAKS" read -a words <<<"$1"
    complete_setting=($(complete -p "${words[0]}"))
    complete_optstring=":abcdefgjksuvprDEo:A:G:W:F:C:X:P:S:"
    while getopts "$complete_optstring" option "${complete_setting[@]:1}"
    do
        case $option in
            F) complete_functon="$OPTARG"
                ;;
            *) # Run around screaming!                    
                ;;
        esac
    done
    COMP_WORDS=("${words[@]}")
    COMP_LINE="$1"
    COMP_POINT="${#COMP_LINE}"
    COMP_CWORD=$((${#COMP_WORDS[@]} - 1))
    "$complete_functon" 
    printf "%s\n" "${COMPREPLY[@]}"
)

Notes:

  • If you're using this as a function for your interactive shell, the initial sourcing isn't needed.
  • complete looks for words split using COMP_WORDBREAKS, so we set IFS to it for read.
  • complete -p prints out the current completion setting in a reusable way, so we can go about parsing the options the way it would.
  • The function uses a subshell (() instead of {}), so your current environment shouldn't be disturbed.
  • 1
    Nice. Thanks. Looking at the answer to the question this was deduped to (unix.stackexchange.com/a/31023/79064), it looks like you can avoid running around screaming using the meta-completion command _command_offset (which will also take care of parsing the options). I haven't had a chance to try it out yet. – Anton Geraschenko Feb 10 '15 at 23:36

Not the answer you're looking for? Browse other questions tagged or ask your own question.