Hi, I’m Erika Rowland (a.k.a. erikareads). Hi, I’m Erika. I’m an Ops-shaped Software Engineer, Toolmaker, and Resilience Engineering fan. I like Elixir and Gleam, Reading, and Design. She/Her. Constellation Webring Published on

Bash Completion

This week, I’ve been exploring how tab completion works in Bash. I knew that it was possible to generate bash completions for command line utilities, but I never knew how it worked.

So I cracked open the source code for click, a python framework for command line applications that had shell completion as a feature. Helpfully, the shell completions were contained in shell_completion.py.The way that click does shell completions is interesting, it uses the application itself to provide completions. I may revisit how this works in the future.

The most helpful line was one that looked like this:

complete -o nosort -F complete_function program_name

With both precise completion_function and program_name determined by the installation of the click application.

complete is a bash builtin, but help complete provides little detail as to how it works. In particular, there’s no mention of the -F flag that click uses.

Info to the Rescue

While help proved to be unhelpful, info a tool I knew about, but hadn’t previously explored, had exactly what I needed.

Section 8.6 Programmable Completion and Section 8.7 Programmable Completion Builtins proved to share exactly what I wanted to know.I’m linking to the web versions, but this information is available through info bash on the command line.

Let’s walk through our line of bash from before:

complete program_name

complete works by registering completions for a particular name, in most cases this is the builtin or program that we want completions for. Once those completions are registered, the particular method defined will work every time completion is invoked, typically by the the tab key.

-o nosort

-o sets a comp-option with specifies extra behavior for complete.

In this case, -o nosort tells complete not to sort the completions array that it receives. In this case, it allows click to precisely control the order of completions that it shows.

-F complete_function

This is most interesting part of complete’s behavior. -F specifies a bash function that will be invoked when completions are asked for on the registered name.

This function receives three arguments from complete:

  • $1 is set to the the name of command which is receiving completions.
  • $2 is set to the word being completed.
  • $3 is set to the word preceding the word being completed.

$3 allows for the invoked function to handle nested commands, such as git’s branch which will complete on available branches for the repo.

When the function finishes, the completions are retrieved from the value of the COMPREPLY array variable.

Putting it together

I now had a mental model of how the completions worked, but I wanted to solidify my knowledge by creating a minimal example.

My named command will be my_echo, a shell wrapper around echo.

#!/usr/bin/env bash
echo $1

With that in mind, I wrote the following bash:

_my_echo_complete() {
  local options=("hello" "hear" "world" "today")
  for option in ${options[*]};
  do
    COMPREPLY+=($(echo "$option" | grep $2));
  done
}

_my_echo_setup() {
  complete -F _my_echo_complete my_echo
}

_my_echo_setup

I hadn’t worked with bash arrays before, so it took some experimentation to learn about () to create an array. Note that the parenthesis are used both for options and next to the += on COMPREPLY.

The [*] operator inside ${options[*]} unpacks the array so that for can iterate through it.

Once I wrote this bash, I added it to a file called complete_function which I then sourced:

$ source complete_function

bash completion example gif

This version still has issues (hitting tab with no input makes grep throw error once for every item in the array), but it’s a working version of tab completion for a program I “wrote”.

I look forward to trying to add tab completion in bash to future tools.Special thanks to Jeff and Kathrin who joined me in my exploration of Bash completion.


Constellation Webring