How Bash completion works
source link: https://www.tuicool.com/articles/eieQnmJ
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
How bash completion works
Published October 6, 2019 by Chris PatuzzoThis is the first of two parts on Bash completion. Part two ishere.
Over the years I’ve developed a command-line tool I use for routine tasks such
as provisioning my machine, generating project templates and managing secrets.
The tool is written in Ruby and I invoke it with the zz
command.
Most of what
it does is fairly
straightforward. The clever bits are usually delegated to something else. For
example, zz provision
is really just a wrapper that installs and runs Chef,
while passing various options to it.
Recently, I added Bash completion to my tool. I’ve wanted this for a while, but
decided to add it now in preparation for secrets
management.
For example, I want to be able to type zz secret --readamaz<TAB><TAB>
and have it complete to zz secret --read amazon/
. Perhaps
hitting <TAB>
again will list all secrets under this path, e.g. username,
password, access_key, etc.
The mechanics
In Bash
, completion is handled through the complete
‘built-in’:
$ type completecomplete is a shell builtin
This command allows you to register a method of completion for a command. For
example, an rgb
command might register its known colors:
$ complete -W "red green blue yellow purple pink orange" color
Setting a hardcoded list of completions
You could then complete color names:
$ color <TAB><TAB>blue green orange pink purple red yellow$ color p<TAB><TAB>pink purple$ color pi<TAB> # completes to pink
The -W
switch configures a static list of completions that are printed in
alphabetical order. It’s just one of the many methods of completion.
Listing completion methods
To see which commands have completion methods, run complete
without arguments:
$ completecomplete -W 'red green blue yellow purple pink orange' colorcomplete -F _nodenv nodenvcomplete -F _rbenv rbenv
Listing all registered completion methods
Here you can see nodenv
and rbenv
support completion. They use the -F
switch to specify functions to handle their completion, namely
_nodenv
and _rbenv
. When you complete one of
these commands, their output is context-aware:
$ rbenv install 2.5<TAB><TAB>2.5.0 2.5.0-rc1 2.5.1 2.5.2 2.5.3
That’s helpful! rbenv
has kindly listed which Ruby 2.5.x
versions are available
to install. We could find this out from rbenv install --list
but that’s
inefficient because we’d have to clear our current command then re-type it.
How completion functions work
When a function is registered as the method of completion with the -F
switch,
it must comply with an ‘interface’ of sorts. When the function is called, Bash
sets some environment variables to be used by the completion function.
They tell
it the contents of the
command-line, the cursor position, etc. For example, $COMP_LINE
contains the
full line that was typed, $COMP_WORDS
is that same line broken into an array
of words and $COMP_POINT
is the cursor’s index position.
In return, the completion function should set $COMPREPLY
to specify which
completions to print for the command.
An example
Everybody loves FizzBuzz, right? Let’s demonstrate Bash completion with a custom function that magically completes the next term in the sequence:
function _fizzbuzz () { length=${#COMP_WORDS[@]} number=$((length - 1)) if ! ((number % 15)); then COMPREPLY=(fizzbuzz) elif ! ((number % 3)); then COMPREPLY=(fizz) elif ! ((number % 5)); then COMPREPLY=(buzz) else COMPREPLY=($number) fi}complete -F _fizzbuzz fizzbuzz
Setting a Bash function to complete fizzbuzz
Our command
is called fizzbuzz
so we name
our completion function _fizzbuzz
, as per the convention. We first set the length
variable to the number of words on the command-line and number
to
one less, since ‘ fizzbuzz
’ itself counts as a word.
We’ve probably all seen FizzBuzz before so let’s skip the modulo logic. The
important part is to set $COMPREPLY
- in this case, to an array of the next
term in the sequence.
Now, if we type fizzbuzz <TAB><TAB>
, Bash completion kicks in and as if by magic
the next term is appended to the current command-line. Our fizzbuzz
command doesn’t actually exist but that doesn’t seem to matter!
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK