Command line completion in bash

If you write a code that's got a bunch of command line options, then it's incredibly useful for the user to be able to use command line completion to see the options and the values they take. It's easy to implement this by writing a function for the bash command completion module, which is almost certainly included in whatever unix-like OS you're using.

Here is an example, for my code neat, annotated to show how to construct one. With this file, tabbing while typing a command line for neat will firstly show what options are available, and then show the values those options can take.

The file begins by opening the function "_neat" and defining the local variables. Bash completion functions are conventionally named _package.


# neat(1) completion
# rw@nebulousresearch.org

_neat()
{
    local cur prev words cword opts helium_options icf_options extinction_options verbosity_options

Initialise bash completion, exit the script if it does not initialise:


    _init_completion || return

Start by setting the default tab completion to nothing:


    COMPREPLY=()

$cur is the current word of the command line. If the current command line ends in a space, then it's null. $prev is the previous word, if there is one. In $opts, specify all the possible command line options that your code recognises:


    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    opts="-i -u -n -e -c -nelow -nemed -nehigh -telow -temed -tehigh -he -icf -v -id -rp --input --uncertainties --n-iterations --extinction-law --helium-data --ionisation-correction-scheme --verbosity --identify -cf --configuration-file --citation"

With those things defined, you can set the tab completion up. This line sets the completion to be any value from within the list defined in $opts:


    COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )

These additional definitions check if a command line option is present, and if it is, then restrict completions to the valid values that the option can take. So, if the user has specified --helium-data and hits tab a couple of times, they'll see "P12 S96", which are the two recognised values for this option.


    case "${prev}" in
        --helium-data|-he)
            helium_options="P12 S96"
            COMPREPLY=( $( compgen -W "$helium_options" -- "$cur" ) )
            return 0
            ;;
	--ionisation-correction-scheme|-icf)
	    icf_options="DI14 KB94 PT92"
	    COMPREPLY=( $( compgen -W "$icf_options" -- "$cur" ) )
            return 0
            ;;
        --extinction-law|-e)
            extinction_options="How Fitz CCM LMC SMC"
            COMPREPLY=( $( compgen -W "$extinction_options" -- "$cur" ) )
            return 0
            ;;
        --verbosity|-v)
            verbosity_options="1 2 3"
            COMPREPLY=( $( compgen -W "$verbosity_options" -- "$cur" ) )
            return 0
            ;;

_filedir is a predefined function which returns files and directories. With _filedir -d, the return values are restricted to directories only. You can also allow the completion only to find files matching a certain pattern. For example, my code alfa operates on plain text or FITS format files, so in its bash completion function I used the command _filedir '@(fit?(s)|ascii|dat|txt)', which allows completions for files ending in .fit, .fits, .ascii, .dat or .txt.


        --input|-i|--configuration-file|-cf)
            COMPREPLY=()
            _filedir
            return 0
            ;;

Some of neat's options don't take any completable values. They either take no value or a number that must be specified. So for these, no completion is permitted:.


        -c|-nehigh|-nelow|-nemed|-n|--n-iterations|-tehigh|-telow|-temed|--citation)
            COMPREPLY=()
            return 0
            ;;
    esac

}

And the file finishes with this command which associates the function _neat with the command neat.


complete -F _neat neat

When bash_completion is initialised, it loads all the completion functions in /usr/share/bash-completion/completions. So, in neat's makefile, the bash completion file is installed to there:


install -m 644 source/bashcompletion $(DESTDIR)$(PREFIX)/share/bash-completion/completions/neat