(Even More) Advanced Bash Completion

In my previous post, I described how to set up tab completion for many common commands using these Bash completion files. This works well for established commands, but it doesn’t work so well for commands that I have written myself.

I use a command called “hc12-console” to connect to 68HC12 microcontrollers over a serial port. The command takes two arguments: the name of a microcontroller to connect to and a file to load. I only have two microcontrollers called “dragon1” and “dragon2”. Therefore, I want to be able to tab complete the first argument to one of those values only. The second argument should be the name of a file that ends in “.load”.

I do this with a function that checks the argument number and then completes it based on a specified list or by limiting the types of files that will be listed.

_hc12console ()
{
	local cur

	COMPREPLY=()
	cur=${COMP_WORDS[COMP_CWORD]}

	# First argument completes with either dragon1 or dragon2
	if [[ $COMP_CWORD -eq 1 ]] ; then
		COMPREPLY=( $( compgen -W "dragon1 dragon2" -- $cur ) )
		return 0
	fi

	# Second argument completes with only files matching *.load
	if [[ $COMP_CWORD -eq 2 ]] ; then
		COMPREPLY=( $( compgen -f -X '!*.load' -- $cur ) )
		return 0
	fi

	# All other arguments will not auto-complete
	return 0
}
complete -F _hc12console hc12-console

I added the script above to a file that my bashrc sources. It associates the function with the shell command, and then instead of manually typing out:

hc12-console dragon1 file.load <enter>

I can type:

hc12<tab> <tab>1 <tab> <enter>

Bad Bash Sourcing

A while back I switched such that my .bash_profile sources my .bashrc instead of the reverse. Based on a few sources, this seems to be the preferred approach. Today I decided to push those files to a few servers that haven’t been updated lately. I transferred my .bash_profile and then my .bashrc:

[email protected]:~$ scp ~/.environment/bash/bash_profile lansky:~/.bash_profile
bash_profile                                  100%  120     0.1KB/s   00:00    
[email protected]:~$ scp ~/.environment/bash/bashrc lansky:~/.bashrc
lost connection

What happened? My .bash_profile sources my .bashrc, but since I hadn’t yet replaced the .bashrc, it still sources my .bash_profile. That means infinite loop and that I am an idiot. SSH, rsync, and scp are all broken. There is no way to remove or replace either of those files without another account. I guess it’s time to open a support ticket at DreamHost.

The moral of the story is to always be careful how you source those files. I usually try to keep an SSH session open on the remote machine when I modify those files in case I break login, but this time I didn’t. A few searches didn’t reveal any solutions. I’d love to know if someone has a way of resolving this without access to another account on the remote machine.

Advanced Bash Completion

Ubuntu has a lot of advanced bash completion features that simplify using the shell. For example, when using the ssh command, I can tab complete server names based on my host file and my ssh config file. It turns out that most of this is accomplished with one bash_completion script. This page has a lot of useful information about the Bash shell and also the very useful script. I’ve found that when I use Fedora in the Xinu lab, I am left typing a lot of this stuff myself. Since I use the same bashrc file on both Dakara and my lab machine (Kastria), I didn’t want to always resource the file so I added this to my bashrc:

# Source global definitions
[ -f /etc/bashrc ]      && source /etc/bashrc
[ -f /etc/bash.bashrc ] && source /etc/bash.bashrc
# enable programmable completion features
if [ -z "$BASH_COMPLETION" \
    -a -r ~/.configuration/bash/bash_completion.caliban ]; then
    BASH_COMPLETION=~/.configuration/bash/bash_completion.caliban
    source $BASH_COMPLETION
fi

First, I source the global definitions, Ubuntu uses /etc/bashrc, and Fedora uses /etc/bash.bashrc. After that, if the bash_completion script was already sourced, $BASH_COMPLETION will be set. I check to see if it is zero length (-z) and then source my own copy of it if it is. Now I have advanced bash completion on both Ubuntu and Fedora.

Your own personal man directory

Recently, I wanted to install a program in my home bin directory, and it had a man page to go with it. I started looking for a way to create my own personal man directory. According to the manual page for man, “man uses a sophisticated method of finding manual page files.” Indeed it does. It turns out that all I had to do was create a man directory inside of my home directory and man would know it was there:

[email protected]:~$ manpath 
/usr/local/man:/usr/local/share/man:/usr/share/man
[email protected]:~$ mkdir man
[email protected]:~$ manpath 
/home/zac/man:/usr/local/man:/usr/local/share/man:/usr/share/man

Then to install the man page, I simply had to create the appropriate directory structure and copy the man page in:

[email protected]:~$ mkdir -p man/en/man1/