The Unix V6 shell and how control flow worked in it

December 1, 2023

On Unix, 'test' and '[' are two names for (almost) the same program and shell builtin. Although today people mostly use it under its '[' name, when it was introduced in V7 along side the Bourne shell, it only was called 'test'; the '[' name was only nascent until years later. I don't know for sure why it was called 'test', but there are interesting hints about its potential genesis in the shell used in V6 Research Unix, the predecessor to V7, and the control flow constructs that shell used.

(This shell is sometimes called the Mashey shell, but the version you can find described in Wikipedia as the 'PWB shell' is rather more elaborate than the V6 sh manual page describes or the V6 sh.c seems to implement.)

The V6 shell didn't have control flow constructs as such; instead it outsourced them to external commands, such as goto(1), exit(1) and if(1). The goto and exit commands worked by changing the seek offset in the shell script, which worked because the V6 shell didn't use buffered input; it read its input line by line (or actually character by character). When they changed the seek offset for a shell script, they caused the V6 shell to read the next line from the new offset, creating either a goto (if the new position wasn't at the end of the file) or an exit (if it was).

The V6 'if' command is even more interesting for us, because it actually isn't a control flow construct as such (although you could use it for control flow in combination with 'goto'). What it does is conditionally execute another command. To quote the manual page's usage section a bit:

if expr command [ arg ... ]

The 'expr' isn't a single argument, it's an expression, as we can see in usr/bin/chk, and the syntax of the expression is quite similar to what the V7 'test' accepts. It's easy to see how this could be translated into the V7 Bourne shell almost equivalent of 'test expr && command [ arg ...]', and if you're translating existing scripts that way, a command with a regular name like 'test' makes sense. If the V7 'test' accepted all of the V6 'if' expressions, you could mostly do it with some straightforward 'ed' commands.

Although I've talked about V6, it turns out that an 'if' command in Research Unix goes back very far. Unix V2 has an if.c that does more or less the same thing as the V6 'if', although I haven't checked the expressions it accepts. The 'if' manual page in V6 cross references 'find' (presumably for a similar way of constructing expressions), but it looks like 'find' appeared later; the earliest I can find is the V5 find.c. So the basic idea of 'run a command if an expression is true' is a very old Unix idea. The V7 Bourne shell 'test expr && command' is merely the culminating form.

(I'm sure Research Unix didn't invent the general idea of executing a command only conditionally. I haven't dug deeply into things like Multics, one obvious source of ideas that Research Unix was drawing on, but I did spot its 'exec_com'. I don't know if earlier operating systems used a standalone 'if' command equivalent or included it as part of a more complicated execution control system.)

Written on 01 December 2023.
« My sysadmin's view of going from Centrex to VoIP (sort of)
Why Unix kernels have grown caches for directory entries ('name caches') »

Page tools: View Source.
Search:
Login: Password:

Last modified: Fri Dec 1 23:11:14 2023
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.