Bash Completion
It would be difficult to not like bash's programmable completion. Tab completion is addictive, and expanding it past files and folders into usernames, hostnames, and, well, anything you can dream up and put in a function, has incredible potential.
It's too bad I've had such a hard time wrapping my head around the
programmable completion toolkit, complete
and compgen
.
Getting There
I have a function that works like cd
, but prepends a specific
directory. Our web files are stored in /some/dir/webapp
, and I want
that directory at my fingertips at all times. Here's the function:
wa() { cd /web/pscpages/webapp/$1 ; }
With this function, wa
brings me to webapp
; wa project1
brings me
to webapp/project1
; and so on. I just provide the full sub-path from
webapp
. Ideally, I would be able to tab-complete directories in
webapp
.
complete
can pull a list of possible completions from a number of
sources: "actions" (like files, directories, commands, shell keywords),
command output, a wordlist separated by some whitespace, or the output
of a bash function, to name a few. What you've typed so far (the
"current word") will be used to filter all the possible completions
returned by that source. Say you've typed "pro" and then hit tab to
autocomplete. The returned completions need to match "pro" at the start
of the string, meaning you can't match against absolute paths like
/some/dir/webapp/project1
.
compgen
can be used to generate a list of possible completions.
Matches will be output one per line, and can be piped around for
transformations just like any other shell command.
Between these two tools, we have everything we need to autocomplete
paths starting in a certain directory. Here's a compgen
that gives us
directories matching a specified string:
compgen -d /some/dir/webapp/
Sample output:
/some/dir/webapp/.svn
/some/dir/webapp/project1
/some/dir/webapp/templates
/some/dir/webapp/images
We need to trim leading directories so "pro" matches "project1." We
should also append /
to the pathnames, since we're always matching
directories:
compgen -S/ -d /some/dir/webapp/ | cut -b 18-
Playing around with compgen
's arguments, we can further filter the
completion list by appending to our string, sort of an implied glob. Use
/some/dir/webapp/p
, and subdirectories starting with "p" will be
returned. This is exactly what we want: compgen
takes care of all the
filtering for us. We have access to a couple special variables to
examine the word the user is expanding. For now, it's enough just to
grab ${COMP_WORDS[COMP_CWORD]}
and append it to our path.
When completions are generated by a function, they're passed back to
complete
by the $COMPREPLY
environment variable. Pulling
this all together, we can now create our completion function:
_webapp() {
local cur
cur=${COMP_WORDS[COMP_CWORD]}
COMPREPLY=( $( compgen -S/ -d /some/dir/webapp/$cur | cut -b 18- ) )
}
All that's left is to tell bash to use this function to complete our
argument to wa
.
complete -o nospace -F _webapp wa
The Fruits
So, that does it. Our original wrapper to cd
, combined with our
autocomplete functionality, looks like this:
wa() { cd /some/dir/webapp/$1 ; }
_webapp() {
local cur
cur=${COMP_WORDS[COMP_CWORD]}
COMPREPLY=( $( compgen -S/ -d /some/dir/webapp/$cur | cut -b 18- ) )
}
complete -o nospace -F _webapp wa
Voila. Tab completion in a directory that's not $PWD
, and it
even works with subdirectories. I hope this makes autocompletion a
little clearer for others.