Computer Laboratory

Bash Completion

There are various ways of performing history completion under bash, all of which have their pros and cons:

1. dynamic-complete-history

The bash function dynamic-complete-history (by default bound to M-TAB). Attempt completion on the text before point, comparing the text against lines from the history list for possible completion matches. (Depending on other variables set you might have to press M-TAB twice to get it to work.) It can easily be bound to some other key combination, which makes it slightly easier to use.

HOWEVER, this results in all possible matches being displayed, it does not allow you to cycle through them with repeated key presses. There is no way to combine it with other functions to get it to cycle through the list.

2. history-search-backward

history-search-backward - this function is not bound to any key by default. If you do bind it to a key (see below for instructions) it allows you to step backwards through your history selecting previous entries which start with the same characters. Less usefully history-search-forward allows you to move forward through your history.

BUT note that repeated pressing of the bound key cycles through completions of whole lines - it can't just complete single words.

3. reverse-search-history

reverse-search-history - this function is bound to Ctrl-r by default. Similar to history-search-backward but is interactive. Type it once and it will display an entry in your history which matches whatever you have already typed. Type more letters and it will look for other history items which match.

4. Programmable completion

This is a feature of recent bashes (2.04 or later - if you type help in a bash shell it will tell you its version) and is powerful but does require considerable customisation.

How to do it:

  1. Bind the function menu-complete to a key, eg Ctrl-] (see below for instructions).
  2. Create an executable script called eg tokenizehist which contains the following:
    fc -ln -999|tr " "\|\>\<\"\& \\n|grep -E "^$2"|sort -u
    and put it somewhere on your PATH.

    This takes your history, breaks it into individual words, throws away those that don't match the second argument (which will be the string we are trying to match on when bash calls this script), and removes duplicates. Alternatively you can create a shell function by putting

    fc -ln -999|tr " "\|\>\<\"\& \\n|grep -E "^$2"|sort -u
    in your .bashrc.
  3. Set the commands that will use tokenizehist to do their completion. Put lines like:
    complete -C tokenizehist lpq
    in your .bashrc. complete is a builtin function of versions of bash later than 2.04. This line says that any text following the lpq command will complete using the output of the tokenizehist script.
  4. Repeated pressing of Ctrl-] after a command for which a completion is set up will then cycle through the possible completions from history.

complete allows you to do other cute things, eg set completion after xon to always be a hostname, completion after cd to always be a directory, completion after cp to always be a filename.

Note that if you set completion as above on lpq then completion will also work on /usr/bin/lpq, but if you set it on /usr/bin/lpq then it won't work on lpq - ie it is simply string based. If there is more than one completion on a string then the latest wins.

Or you could use some hack like this in ~/.bashrc to do a complete on all the commands in your PATH:

complete -C tokenizehist `for f in \`echo $PATH|tr : " "\`; do ls -1F $f|grep \*|tr -d \*; done 2>/dev/null`
which works ... but does add a noticeable delay to startup :-(

complete -p lists all the completions set up. complete -r removes all current completions.

Appendix: How to bind a key to a function

  1. Put instructions in a file, eg ~/.bashkeybindings The instructions should be one per line, eg
    "\C-]": menu-complete
    "\C-[": dynamic-complete-history
    "\C-p": history-search-backward
  2. Put the line
    bind -f ~/.bashkeybindings 
    in ~/.bashrc

bind -P will list all the key bindings.