How to make autocomplete for any bash commands

Stolen and awkwardly translated from here, with a few additions.

Simple version — static list

Suppose there is a command dothis. Let’s create a file dothis-completion.bash:

#/usr/bin/env bash
complete -W "now tomorrow never" dothis

Run . ./dothis-completion.bash, and now pressing Tab will produce:

$ dothis <tab><tab>
never     now       tomorrow

$ dothis n<tab><tab>
never now

The -W flag specifies to take a word list. -F func — call a function and use its output as the completion list. -C cmd — same, but execute a command.

-A action — autocomplete with:

  • alias aliases (= -a).
  • arrayvar array variable names.
  • binding readline bindings.
  • builtin builtin command names (= -b).
  • command command names (= -c).
  • directory directories (= -d).
  • disabled disabled builtin commands.
  • enabled enabled builtin commands.
  • export exported variables (= -e).
  • file files (= -f).
  • function functions.
  • group groups (= -g).
  • helptopic built-in help topics.
  • hostname hosts (host file is specified by the HOSTFILE variable).
  • job jobs (= -j).
  • keyword shell keywords (= -k).
  • running running tasks.
  • service services (?) (= -s).
  • setopt valid arguments for the shell -o option.
  • shopt options for shopt.
  • signal signals.
  • stopped stopped jobs.
  • user users (logins) (= -u).
  • variable variables.

For example, complete -A directory dothis will complete with directory names.

Dynamic completion

Let’s change the script:

#/usr/bin/env bash
_dothis_completions()
{
  COMPREPLY+=("now")
  COMPREPLY+=("tomorrow")
  COMPREPLY+=("never")
}

complete -F _dothis_completions dothis

This produces the same result as the original version — three completion options. However, if we type dothis ne<tab><tab>, we get all three options instead of the expected single never.

Let’s fix this. In the function called for autocompletion, the following variables are available:

  • COMP_WORDS array of words entered after the program name;
  • COMP_CWORD index of the word in the previous array where the cursor currently is;
  • COMP_LINE the current command line.

Additionally, the built-in compgen function, which accepts the same flags as complete, can produce a completion list filtered by the beginning of a word. For example, compgen -W "now tomorrow never" n outputs now and never.

Therefore, the basic functionality can be obtained like this:

#/usr/bin/env bash
_dothis_completions()
{
  COMPREPLY=($(compgen -W "now tomorrow never" "${COMP_WORDS[1]}"))
}

complete -F _dothis_completions dothis

Example of completing the -u flag with user names:

_dothis_completions()
{
  local cur prev opts
  COMPREPLY=()
  cur="${COMP_WORDS[COMP_CWORD]}"
  prev="${COMP_WORDS[COMP_CWORD-1]}"

  if [[ ${prev} == -u ]] ; then
    COMPREPLY=( $(compgen -u -- ${cur}) )
    return 0
  fi
}

All variables available in autocompletion:

  • COMP_CWORD Index of the current word in ${COMP_WORDS}
  • COMP_LINE The entire command line
  • COMP_POINT Cursor position relative to the beginning of the command line. If the cursor is at the very end, this equals ${#COMP_LINE}.
  • COMP_WORDBREAKS Set of characters considered word delimiters for autocompletion (do not modify it — it’s a global variable!).
  • COMP_WORDS Array of words from the current ${COMP_LINE}.
  • COMPREPLY Array of words for autocompletion — the output of the completion script.