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.