Bash has a programmable completion feature that allows the command line to be completed from a partial command with a press of the <Tab> key. This feature is very useful and provided by many software packages.

If you run ls /usr/share/bash-completion/completions, you will find many completion scripts provided by those packages. For example, the completion script provided by package systemd is located at /usr/share/bash-completion/completions/systemctl.

However, sometimes completions are not available due to:

• Some binary packages don’t come with completion scripts.

• Programs are compiled from source which doesn’t have a completion script.

• User-defined functions and aliases.

Fortunately, we can write our own completion scripts for any command we use.

Problem Definition

We illustrate Bash completion by defining the problem. We have a command mycmd, which accepts a single argument from choices (bob, jack, john). We want Bash to auto-complete the argument as we type.

To solve this problem, we need to write a Bash completion script and put it at ~/.bash_completion. The script calls the complete builtin to add a completion.

Depending on your settings, you may need to press <Tab> twice for completion. But in this text we just write one for conciseness.

Solutions

Now we give solutions to the above problem.

Solution 1

Script:

complete -W 'bob jack john' mycmd


Output:

$mycmd <Tab> bob jack john$ mycmd j<Tab>
jack  john


This is probably the most basic (but useful) settings we can have. The -W option specifies a list of argument candidates. And the shell is clever enough only giving candidates with matching prefix.

Solution 2

Script:

_mycmd () {
}
complete -F _mycmd mycmd


Output:

$mycmd <Tab> bob jack john$ mycmd j<Tab>
bob   jack  john


To add more flexibility, we can use a completion function.

The function accepts these inputs:

• $1: The name of the command whose arguments are being completed. • $2: The word being completed.

• $3: The word preceding the word being completed on the current command line. And sets the following output: • COMPREPLY: An array variable from which Bash reads the possible completions, each element containing one possible completion. The script above doesn’t really use the inputs. It just demonstrates how to use a completion function. Therefore it’s not as clever as the first solution, which is shown by always returning bob even if the argument starts with a j. Solution 3 Script: _mycmd () { candidates=(bob jack john) COMPREPLY=() for candidate in${candidates[@]}; do
if [[ "$candidate" == "$2"* ]]; then
COMPREPLY+=("$candidate") fi done } complete -F _mycmd mycmd  Output: $ mycmd <Tab>
bob   jack  john
cancel
...
$mysystemctl list-<Tab> list-dependencies list-jobs list-sockets ...  Post-Extend Existing Completions You can even add some post-processing to the COMPREPLY array resulted from the inner function: _mysystemctl () { . /usr/share/bash-completion/completions/systemctl && _systemctl for i in${!COMPREPLY[@]}; do
COMPREPLY[$i]="${COMPREPLY[$i]}"_suffix done } complete -F _mysystemctl mysystemctl  Test its output: $ mysystemctl <Tab>
cancel_suffix
...
$mysystemctl list-<Tab> list-dependencies_suffix list-jobs_suffix list-sockets_suffix ...  The bash-completion Project The bash-completion project (Homepage) contains a lot of useful completion scripts from commonly used commands. It also provides helper functions to make it easier to write a custom completion script. For example, this is a solution to the same problem using helper function _init_completion: _mycmd () { local cur prev words cword _init_completion || return candidates=(bob jack john) COMPREPLY=() for candidate in${candidates[@]}; do
if [[ "$candidate" == "$cur"* ]]; then