Bash programmable completion
Posted 26 July 2009 in bashA 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.
Directory-only completion
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/
complete
's -d
option restricts filename completion to directories.
Filtering filenames
Suppose you're dealing with a command that deals with documents with certain extensions, such as unzip
. 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
The -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!
Matching usernames
complete
has many built-in completion possibility sources, all of which can be specified using the -A
switch. The -f
and -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
Limitless possibilities
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!