Archive for November 27th, 2008

deferred aliasing

Thursday, November 27th, 2008

Shell users like to tailor their environment to speed up common tasks. The alias mechanism is very handy for this, you bind a short idiom to the sequence you run a lot. For instance, I have this line in my alias list:

alias lh='ls --color -Flah'

Download this code: alias_lh

Having done that, you can also rig up a system of pulleys so that you can take the whole thing with you wherever you go.

All is well so far, but now comes the icky part. If you take this bundle of joy to murky neighborhoods like BSD or commercial Unices you’ll find out that the userland isn’t the same everywhere, even though you have the shell. BSD (DesktopBSD), for instance, doesn’t support the whimsical –color switch, they like monochrome.

So what to do? What I would like to have is the same alias bound to something that works on the given platform, let it degrade gracefully. The only way to know that is to test for it, then bind the alias accordingly. That’s what happens here, I try the incantation I want and if that doesn’t work, I use the failsafe one.

setalias() {
	if eval $2 &>/dev/null; then
		eval "alias $1='$2'"
	else
		eval "alias $1='$3'"
	fi
}
 
setalias "lh" "ls --color -Flah" "ls -Flah"

Download this code: alias_test

But this is still a bit lacking. You obviously set up the aliases to run at shell startup, not manually. But there are instances when you might need a new shell, but you might have high io latency, or the io might be locked up through a system error. In such a case, you really just want to do as much as is necessary to start the shell, without doing a lot of io stuff in the startup files. So running ls and things like that for the purpose of setting up an alias is to be avoided.

Here’s where an idea from compilers can help. First, let’s think about the setalias function as code to be executed. This code runs on shell startup. But it doesn’t have to. We could just as well bind the alias to the function code itself, to make it run the first time the alias is executed. That’s what happens here, the shell starts and binds the alias to a string. The first time the alias is executed, it runs the test and then does the binding. And then runs the actual command we wanted to run.

setalias() {
	alias "$1"="if eval $2 &>/dev/null; then
		eval \\"alias $1='$2'\\"
	else
		eval \\"alias $1='$3'\\"
	fi; eval $1"
}
 
setalias "lh" "ls --color -Flah" "ls -Flah"

Download this code: alias_deferred

It’s like a just-in-time compiler, we do some extra work the first time, but from then on it’s all set up.

There’s a lot more you can do with eval, so go nuts!