A long time ago I read a blog entry about a shell called zsh that supported command line completion like I had never seen before. Don't get me wrong, I've been using filename completion for years, but I had never seen a shell perform filename completion across the network over an SSH connection before, or complete a manpage name, or complete a long option to a program (like mplayer's obscure
-noflip-hebrew-commas option). The powers of zsh impressed me, but I never got around to switching to it. Years later, now, I've discovered that my current shell, bash, actually does support programmable command line completion, and I've become so impressed that I wanted to share some of the things that I've found.
First, you need to know what command to use to actually set up programmable completion in bash. It's called
complete, surprise, and I'll give you a simple example of how to use it. Let's say that you've just downloaded that totally rockin' piece of software in a zipfile. It's named "baloo-kit-latest.zip", and when it's extracted it goes into a single directory named "baloo-kit-5.0". Now, bash isn't too bright, so when you try to complete the directory name, bash unhelpfully fails to match only the directory:
$ cd b <tab> $ cd baloo-kit- <tab> <tab> baloo-kit-5.0/ baloo-kit-latest.zip
Thanks for nothing, bash! But just wait a second, it gets awesome:
$ complete -d cd $ cd b <tab> $ cd baloo-kit-5.0/
-d option restricts filename completion to directories.
Suppose you're dealing with a command that deals with documents with certain extensions, such as
complete has a switch,
-X, with which you can specify filtering. The
-X switch will exclude whatever you specify, but you can invert the matching (so that instead of excluding you're including) by prefacing the pattern with "!".
$ complete -f -X '!*.zip' unzip $ unzip <tab> $ unzip baloo-kit-latest.zip
-f switch specifies filename completion, and
-X excludes everything from the results that doesn't match the pattern specified. Be careful to quote everything properly!
complete has many built-in completion possibility sources, all of which can be specified using the
-A switch. The
-d switches are actually convenient shorthand equivalents for
-A file and
-A directory, respectively. As another simple example, I know that the
usermod command deals not with files but users, which is something bash can provide completion for:
$ complete -A user usermod $ usermod <tab> alpha bravo charlie root
While bash provides a lot of built-in possibility sources, it can't possibly cover everything, which is why
complete also allows you to run a command (the
-C switch) or a shell function (the
-F switch). With these, it's possible to complete hostnames from the SSH known_hosts file, to complete remote pathnames over an SSH connection, to complete git branch and tag names, or anything else. You can find a bunch of smart completions already written over at the Bash-Completion project, and git has a thorough set of completions included in its distribution.
Smart command line completion can make you faster and more effective, so get after it!