Archive

Tag Archives: bash

A while ago I wrote up a method for switching between /etc/hosts files. That post is pretty popular and clearly been useful to a few people.

Since then I’ve actually extended this method into a tool that can manage multiple /etc/hosts files. Using this new method you can have a folder that you can add hosts configurations to, and those files become available for switching into /etc/hosts straight away.

There’s no reason why an adaptation of this method wouldn’t work in Linux as well.

Here’s how you do it:

1. Create the host configuration directory

We’ll store all the /etc/hosts type files in /usr/local/share/hs.

You’ll need to do all of this as a root user. So switch to root using sudo -s first (you’ll need to be an administrator to do this).

$ mkdir -p /usr/local/share/hs
$ chmod 755 /usr/local/share/hs

Now to start with, let’s make a copy of the existing /etc/hosts file in this directory.

$ cp /etc/hosts /usr/local/share/hs/hosts.normal
$ chmod 744 /usr/local/share/hs/hosts.normal

Then make the real /etc/hosts a symbolic link to that new file.

$ ln -sf /usr/local/share/hs/hosts.normal /etc/hosts

Any additional host configurations in this folder need to be named using a similar hosts.* pattern.

2. Create the host switching script

Take the following bash script and save it to a file located at /usr/local/bin/hs

#!/bin/bash

if [ $1 ]; then
	HFILE=hosts.$1
else
	HFILE=hosts.normal
fi

# Change this if you put your host files elsewhere
HPATH="/usr/local/share/hs/"

echo "Attempting to switch host file to $HPATH$HFILE"

if [ ! -e $HPATH$HFILE ]; then
	echo "--> File does not exist!"
	echo "--> Exiting with status 1"
	exit 1
fi

if [ -d $HPATH$HFILE ]; then
	echo "--> File is a directory!"
	echo "--> Exiting with status 2"
	exit 2
fi

if [ -h $HPATH$HFILE ]; then
	echo "--> File is a symbolic link!"
	echo "--> Exiting with status 3"
	exit 3
fi

if [ ! -h /etc/hosts ]; then
	echo "--> Current hosts file is a real file!"
	echo "--> Exiting with status 4"
	exit 4
fi

# Link it up
sudo ln -sf $HPATH$HFILE /etc/hosts

# Flush the system-wide DNS cache
if [ -x /usr/bin/dscacheutil ]; then
	# Mac OS X 10.5
	/usr/bin/dscacheutil -flushcache
elif [ -x /usr/bin/lookupd ]; then
	# Mac OS X Less than 10.5
	/usr/bin/lookupd -flushcache
elif [ -x /usr/sbin/lookupd ]; then
	# Mac OS X Less than 10.5 (alternative)
	/usr/sbin/lookupd -flushcache
fi

echo "--> Done"

The script does some basic validity checking on the file and also makes sure that /etc/hosts is just a symbolic link and not a real file.

You will need to adjust the permissions on this script so that only admins can run it.

$ chmod 750 /usr/local/bin/hs

You also need to make sure that /usr/local/bin is in your PATH so that the hs command can be found. We can test that.

$ echo $PATH

… will output something like …

/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin

If it’s not there then you need to add it in your users ~/.profile file. Add the following line to that file, you can create the file if it isn’t there.

export PATH=/usr/local/bin:$PATH

Now reload your profile and the hs command should be available.

$ . ~/.profile

You could also just close and open your terminal window to reload your profile.

3. Trying it out

The hs command takes one argument. The argument is used to specify the hosts configuration file to be loaded from /usr/local/share/hs. If no argument is given, then the assumed file is hosts.normal.

So for our first test we should add another hosts configuration.

$ cp /usr/local/share/hs/hosts.normal /usr/local/share/hs/hosts.test

Edit the new hosts configuration file to your liking.

Now we can try the switching command to switch to that configuration. You will need to supply your password if you are not already running as root.

$ hs test
Attempting to switch host file to /usr/local/share/hs/hosts.test
Password:
--> Done

The symbolic link at /etc/hosts should now be pointing to /usr/local/share/hs/hosts.test.

$ ls -l /etc/hosts
/etc/hosts -> /usr/local/share/hs/hosts.test

Now try switching back to your normal configuration.

$ hs
Attempting to switch host file to /usr/local/share/hs/hosts.normal
Password:
--> Done

Any more configurations that you add to /usr/local/share/hs are available this way.

Neat, huh?

4. Fancy schmancy tab completion

So that’s all great until you have 10 different hosts config files and you can’t remember their names.

The simple solution is to register additional commands so that command line tab completion can be used. The following additional script registers all the available hosts configs as separate commands. So instead of typing hs test we can type hstest. More importantly we can type hs then hit the “tab” key twice and get a full list of all available commands (one per configuration file).

Hard to explain, easier to just do. Add the following lines of code to the end of your ~/.profile file.

# Setup hs* commands
HSPATH=/usr/local/share/hs
if [ -d $HSPATH ]; then
	HSFILES=(`ls $HSPATH`)
	HSCOUNT=${#HSFILES[*]}
	for (( HSI=0 ; $HSI < $HSCOUNT ; HSI=$HSI+1 )); do
		HSFILE=${HSFILES[HSI]}
		HSFILE=${HSFILE:6}
		alias hs$HSFILE='hs '$HSFILE
	done
fi
unset HSPATH HSFILES HSCOUNT HSI HSFILE

This script loops through all the files in /usr/local/share/hs and adds a command for each. Once that is added, reload your profile to make the script work.

$ . ~/.profile

Now you should be able to type hs then hit the “tab” key twice and get a list of the available commands.

$ hs TAB TAB
hs            hsnormal      hstest

If you add any new configuration files to /usr/local/share/hs you need to reload your profile for it to turn up as a new command (or just relaunch your terminal window).

Enjoy!