Tuesday, April 22, 2008

This is Only a Test

Here and there, you'll see the syntax
if [ -f scriptname -a -x scriptname ]
if [ -f file1 -o -f file2 ]
These should, nowadays, be re-written like this:
if [ -f scriptname ] && [ -x scriptname ]
if [ -f file1 ] || [ -f file2 ]
Here's an opportunity for a little shell archaeology. In the old days, when men were men and giants walked the earth, test was a stand-alone executable. ( [ foo ] is a synonym for test foo ).

Actually, it still is:
$ ls /usr/bin/test
-rwxr-xr-x 1 root root 23036 2007-09-29 06:51 /usr/bin/test
In fact,
$ ls -l /usr/bin/[
-rwxr-xr-x 1 root root 25024 2007-09-29 06:51 /usr/bin/[
(Now you see how [ foo ] works in older shells.)

Old shells weren't nearly as powerful as newer ones, and in order to do even a simple test operation, you had to invoke a separate executable.

You can argue this is clean and minimalist, giving the shell only the roles that it has to have -- indeed, people make analogous arguments about microkernels -- but it's not efficient: to invoke the test sub-process, the kernel needs to fork() a subshell, and then exec() a new executable. To make things even messier, both these are kernel calls, which offers the risk of a process swap. All for a simple test.

Still, if you have to do that, it's more efficient to do both tests inside the new, test process than to return and invoke a second sub-process to do the second test. That is, [test1 -a test2 ] means "Do test1 and test2, and make me use an ugly syntax to tell you that while you're at it."

In fact, the syntax of test can get quite messy, but never mind that: you don't need it any more. The shell now offers its own, built-in versions of test, &&, and || .
$ type test
test is a shell builtin
Moreover, || and && both provide short-cut evaluation, just as in C.

No comments: