Bash has quite a nice feature, you can write a command in a console, and then
press <TAB> twice. This should show you all possible arguments you can use for
this command.
In our Liquid Galaxy software stack we have a script which allows us to connect with ssh to one of our installations using a special tunnel. This script is complicated, however the main usage is simple. The command below will connect me to my Liquid Galaxy machine through a special proxy server.
lg-ssh szymon
The szymon part is the name of my LG,
and it is taken from one of our chef node definition files.
This script also takes huge number of arguments like:
lg-ssh --chef-directory --ssh-identity --ssh-tunnel-port
There are two kinds of arguments: one is a simple string, one begins with --.
To implement the bash completion on double <TAB>, first I wrote a simple python
script, which makes a huge list of all the node names:
#!/usr/bin/env python
from sys import argv
import os
import json
if __name__ == "__main__":
pattern = ""
if len(argv) == 2:
pattern = argv[1]
chef_dir = os.environ.get('LG_CHEF_DIR', None)
if not chef_dir:
exit(0)
node_dirs = [os.path.join(chef_dir, "nodes"),
os.path.join(chef_dir, "dev_nodes")]
node_names = []
for nodes_dir in node_dirs:
for root, dirs, files in os.walk(nodes_dir):
for f in files:
try:
with open(os.path.join(root, f), 'r') as nf:
data = json.load(nf)
node_names.append(data['normal']['liquid_galaxy']['support_name'])
except:
pass
for name in node_names:
print name
Another thing was to get a list of all the program options. We used this simple one liner:
$LG_CHEF_DIR/repo_scripts/lg-ssh.py --help | grep ' --' | awk {'print $1'}
The last step to make all this work was making a simple bash script, which uses the python script, above, and the one liner.
_lg_ssh()
{
local cur prev opts node_names
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts=`$LG_CHEF_DIR/repo_scripts/lg-ssh.py --help | grep ' --' | awk {'print $1'}`
node_names=`python $LG_CHEF_DIR/repo_scripts/node_names.py`
if [[ ${cur} == -* ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
COMPREPLY=( $(compgen -W "${node_names}" -- ${cur}) )
}
complete -F _lg_ssh lg-ssh
complete -F _lg_ssh lg-scp
complete -F _lg_ssh lg-ssh.py
Now I just need to source this file in my current bash session, so I've added the line below in my ~/.bashrc.
source $LG_CHEF_DIR/repo_scripts/lg-ssh.bash-completion
And now pressing the <TAB> twice in a console shows a nice list of completion options:
$ lg-ssh Display all 129 possibilities? (y or n) ... and here go all 129 node names ...
$ lg-ssh h ... and here go all node names beginning with 'h' ...
$ lg-ssh -- .. and here go all the options beginning with -- ...
The great feature of this implementation is that when someone changes any of the script's options, or changes a chef node name, then the completion mechanism will automatically support all the changes.