deferred aliasing

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'

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"

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"

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!

:: random entries in this category ::

3 Responses to "deferred aliasing"

  1. aron says:

    Quite nice!
    Now I know why my BSD-zealot boss just smiles faintly when I mention color in terminals.
    Really creative to transfer concepts to different contexts like that.

    Do you have an equallly nifty way to make it shell agnostic as well? =)

  2. numerodix says:

    It probably will run unmodified in zsh. That's the only other shell I've ever used. If the csh variants have an eval then maybe you could translate this.

  3. [...] past how it’s a good idea to avoid unnecessary cpu/io while initializing the shell by doing deferred aliasing. That solves the alias problem, but I also have a bunch of bash code in terms of functions. So I [...]