2008-09-16
A Unix without a test program
Courtesy of a recent entry, I was
reading over my dotfiles when I stumbled over a remnant of a rather
peculiar and annoying Unix (unfortunately, I no longer remember which
one it was). This Unix was notable for a simple reason: it didn't have
a test program.
You might sensibly ask how on earth anything that can possibly call
itself a Unix would be missing such a basic thing; after all, without
test a lot of scripts don't work at all, since test is also known as
'[', which is commonly used in Bourne shell condition checking. The
answer is simple: the people behind this Unix had made test a shell
builtin, so all of the shell scripts
still worked. They just hadn't set up a /bin/test program, presumably
because they didn't expect any sensible program to run test directly.
(This is slightly less crazy than it seems; if you have a a test
program and also a test shell builtin, you have to make sure that the
two behave exactly the same. If you expect the test program to never
be used, removing it simplifies your life and means that you don't have
to keep it in sync with your improvements to the shell builtin.)
As it happens, there is one sort of sensible program that does exactly
this: non-standard shells. My
shell doesn't have test as a builtin, so I needed an actual test
program; when this Unix didn't have one, my dotfiles became moderately
unhappy.
(My solution was to make a test Bourne shell script that just ran the
shell's builtin test, which is what this particular Unix vendor should
have done for /bin/test in the first place.)
2008-09-10
A Unix shell trick
This is the sort of trick where I present the code first and explain it afterwards:
arch=`(PATH=$HOME/bin:/local/bin:$PATH arch || echo unknown) 2>/dev/null`
(This has been slightly modified to keep the line length down; in the
real version, more things are added to $PATH. Note that this may be
word-wrapped in your browser.)
This sets the $arch variable to the output of the arch command,
if there is one to be found in any number of places, and otherwise
gives $arch the value of 'unknown', all in one line.
It's worth unpacking this into its component parts (somewhat
abbreviated) in order to see how it works:
PATH=$HOME/bin:/local/bin:$PATH arch- Search for an
archcommand using a temporarily augmented and altered$PATH. ... || echo unknown- If we failed to find an
arch, supply a default value; a command that isn't found is a false condition, just as if the command itself failed. (We assume thatarchitself will never fail.) (...) 2>/dev/null- Discard any complaint from the shell about being
unable to find
arch.
(Using just '$PATH=... arch 2>/dev/null' wouldn't suppress the
'arch: not found' error message, because the error message comes from
the shell, not from a theoretical arch. We have to use a subshell in
order to be able to redirect the shell's own error message.)
This is the kind of thing that makes programming in the Bourne shell fun and interesting, and I say that non-sarcastically.
Sidebar: why I do this
Why I do this requires slightly more explanation. I have a single
generic .profile that I use on all of the systems I have accounts
on, and on some of them my home directory is shared across multiple
architectures, which means that I need to add an architecture-specific
directory to my $PATH (for architecture dependent binaries), which
means that I need to know what the local architecture is.
(By local tradition, 'architecture' here includes the operating system as well as the chipset; typical values are things like 'solaris-sparc'.)
There is a vague local tradition that there is an arch command that
prints out a string suitable for this. However, not all systems follow
this tradition, and not all systems put arch in the same place, and
sometimes I need to fake it with a $HOME/bin/arch (if, for example,
I am in a multi-architecture environment that doesn't follow this
tradition). So determining the local architecture requires dealing
with all of these possibilities, hence the one-liner.