Bash completion scripts generally only use COMP_WORDS and COMP_CWORD to determine the current and previous words on the command lines. However there are helper functions that use some of the more obscure variables like COMP_LINE and COMP_POINT. All in all, these are used to generate a list of completions which are stored in the array COMPREPLY and then printed to the terminal. So if we want the output of tab-completion, we need to access COMPREPLY after the completion script has populated it.
Here’s a very simple implementation:# load bash-completion helper functions
source /usr/share/bash-completion/bash_completion
# array of words in command line
COMP_WORDS=(git a)
# index of the word containing cursor position
COMP_CWORD=1
# command line
COMP_LINE='git a'
# index of cursor position
COMP_POINT=${#COMP_LINE}
# execute completion function
# FYI: _xfunc is a helper function for loading and calling functions from
# dynamically loaded completion files that may not have been sourced yet
_xfunc git _git
# print completions to stdout
printf '%s\n' "${COMPREPLY[@]}"
That works alright, but what if things are more complicated? What if we want to perform programmatic tab-completion for other commands? For commands that do not load completions dynamically? For more complex command lines? What if we want to more accurately reflect real-world tab-completions?
Here is such a function:#
# Author: Brian Beffa <brbsix@gmail.com>
# Original source: https://brbsix.github.io/2015/11/29/accessing-tab-completion-programmatically-in-bash/
# License: LGPLv3 (http://www.gnu.org/licenses/lgpl-3.0.txt)
#
get_completions(){
local completion COMP_CWORD COMP_LINE COMP_POINT COMP_WORDS COMPREPLY=()
# load bash-completion if necessary
declare -F _completion_loader &>/dev/null || {
source /usr/share/bash-completion/bash_completion
}
COMP_LINE=$*
COMP_POINT=${#COMP_LINE}
eval set -- "$@"
COMP_WORDS=("$@")
# add '' to COMP_WORDS if the last character of the command line is a space
[[ ${COMP_LINE[@]: -1} = ' ' ]] && COMP_WORDS+=('')
# index of the last word
COMP_CWORD=$(( ${#COMP_WORDS[@]} - 1 ))
# determine completion function
completion=$(complete -p "$1" 2>/dev/null | awk '{print $(NF-1)}')
# run _completion_loader only if necessary
[[ -n $completion ]] || {
# load completion
_completion_loader "$1"
# detect completion
completion=$(complete -p "$1" 2>/dev/null | awk '{print $(NF-1)}')
}
# ensure completion was detected
[[ -n $completion ]] || return 1
# execute completion function
"$completion"
# print completions to stdout
printf '%s\n' "${COMPREPLY[@]}" | LC_ALL=C sort
}
Fox example get_completions 'apt-get '
outputs the following:
autoclean autoremove build-dep changelog check clean dist-upgrade download dselect-upgrade install purge remove source update upgrade
get_completions 'docker p'
outputs the following:
pause port ps pull push
get_completions 'vagrant box '
outputs the following:
add help list outdated remove repackage update