Tuesday, February 4, 2014

Tab Completion on Cumulus Linux

This film could have ended much differently

if Jerry were running Cumulus Linux 

The TAB key on my keyboard gets a lot of use. Whether I'm looking at a bash prompt on a *NIX system or logged into a router's CLI, I almost never type whole commands.

In the bash shell, tab completion capabilities are usually limited to helping complete:

  • shell built-in commands
  • external executables found in $PATH
  • file names
  • directory names
Completion in bash doesn't help with things like command line arguments to various commands, but it is (sometimes) smart enough to not offer filenames as completion options to the 'cd' command, choosing instead to only offer directories.

Network devices, on the other hand, tend to have really rich inline help / command completion stuff, and I live by it.

Rather than typing abbreviated commands, I prefer to let the system help me type the whole thing, partly because it eliminates errors, and partly because I usually can't remember the exact syntax for a given platform. Cisco's godawful platform-dependent mac-address-table vs. mac address-table comes immediately to mind as something that always seems to take more than one attempt.

So, rather than typing this:
ROUTER#sh ip bg vpnv4 vr GREEN nei A.B.C.D received-    

I tend to do this:
ROUTER#show ip bg<TAB>
ROUTER#show ip bgp vp<TAB>
ROUTER#show ip bgp vpnv4<TAB>
ROUTER#show ip bgp vpnv4 vr<TAB>
ROUTER#show ip bgp vpnv4 vrf GREEN nei<TAB>
ROUTER#show ip bgp vpnv4 vrf GREEN neighbors A.B.C.D re<TAB>
ROUTER#show ip bgp vpnv4 vrf GREEN neighbors A.B.C.D received-<TAB>
ROUTER#show ip bgp vpnv4 vrf GREEN neighbors A.B.C.D received-routes
This is pretty helpful, and an improvement over the bash shell where you often must abandon the command line for man pages in order to figure out the options for a given command.

Imagine my surprise when I found myself using pretty-full-featured tab completion in Cumulus Linux!

For example, when I started up the routing services for the first time, I instinctively did this:
#service qu<TAB>
#service quagga sta<TAB>
#service quagga start
Checking the CDP/LLDP neighbors table also supports completion:
#lldpcli nei<TAB>
#lldpcli neighbor sh<TAB>
#lldpcli neighbor show
If I wasn't sure about an argument, or if more than one argument matched, a second press of <TAB> offered the whole list of possibilities in the way you'd expect from bash, similar to the '?' on Cisco IOS.

This is really nice, and so seamlessly integrated that I didn't even notice I was using it at first. Then I noticed, and was mystified: What is this wizardry?

Well, here's how it works.

Suppose we've got an executable mycommand, which understands two arguments "-A" and "-B".

In order to get bash completion for mycommand, we need to drop a bash completion script in /etc/bash_completion.d/ (a directory I'd never even noticed before.) The script should get sourced by your shell at startup time, and it does two things:
  1. It loads a module into the shell.
  2. It associates the module with the command in question for completion purposes.
So, here's a simple example, for mycommand.
[chris@poetaster]$ cat /etc/bash_completion.d/mycommand.bash
    COMPREPLY=( $(compgen -W "-A -B" -- ${cur}) )
    return 0
complete -F _mycommand mycommand
Now, we're rewarded with the following:
[chris@poetaster]$ myc<TAB>
[chris@poetaster]$ mycommand <TAB>
[chris@poetaster]$ mycommand -<TAB>
<audible bell>
[chris@poetaster]$ mycommand -<TAB>
-A  -B 
[chris@poetaster]$ mycommand -A
Unlike Cisco IOS, the bash prompt in Cumulus Linux executes completion without printing extraneous lines, which I appreciate.

Of course, the completion routines baked into CumulusLinux are much more complicated than my simple "-A -B" example here. They understand the context of the command, where files are required vs. switches, when one option makes another obsolete, interface names, ip addresses, etc...

The completion feature wasn't mentioned in any of the documentation I read. It's a small thing, and they just did it without making a big deal. But it's really helpful, and I think it will help make lots of network folks much more comfortable in the Linux shell than they might otherwise have been.

Also, I'm glad they did it because I had no idea this existed, and I'm already thinking about ways to bake it into stuff I work on every day. Particularly virsh. I want command completion to help me type the names of VMs.


  1. You should try the Fish shell (http://fishshell.com/)! You will be amazed :)

  2. This comment has been removed by a blog administrator.

  3. I've never heard of Cummulus Linux, I'll have to check it out. And, like others, I enjoyed my brief visit to your site. ;)

  4. Been part of Junos since day 1. Not only can it tab complete knows Junos commands but also user created variables.

    For instance if you created two firewall filters - cust_Pepsi_West and cust_Coke_East - you could do

    sh [ tab ] fir [ tab ] fil [ tab ] cu [ tab ] P [ tab ]

    In long form it would be
    show firewall filter cust_Pepsi_west

    1. I agree that it's awesome to have this feature, and including user-created strings in the completion is a nice touch.

      But I don't think it's all that remarkable when it's a proprietary CLI parser doing that work. The bit that really jumped out at me in this case is that it's a /linux/distribution/. That was bash doing the inline help for options required by other binaries, not a monolithic command interpreter.