A bit more trivia on the Unix V6 shell and its control flow

December 3, 2023

Over on the Fediverse, I posted about how the V6 'goto' and 'exit' worked, and got a good question in response, namely how did 'goto' and 'exit' get hold of the file descriptor for the script that the V6 shell was executing. The answer turns out to be that the V6 shell always read from standard input (fd 0). If it was running a script, it arranged to open the script with file descriptor 0 (standard input), which it passed on to all children as usual.

(In my Fediverse answer I said it used 'dup()', but the V6 sh.c says I'm wrong. V6 sh.c simply closed fd 0 just before it open()'d the script, insuring that the open() would give it fd 0. There was also special code to 'read' from the -c command line option instead of standard input.)

Obviously this has certain limitations, like you'd better not write shell scripts using programs that read their standard input as a side effect of other operations (at least not without a '</dev/null' or the like). But as Gaelan Steele noted, it's a very early Unix way to handle the whole thing.

Also, Norman Wilson noted that this is where we get the ':' command to do nothing, which would later be one of the ways of writing comments in shell scripts. However, V6 isn't where either ':' or 'goto' appear; a version of 'goto' can be found as far back as V2's goto.c, and just as in V6 it searches through standard input for a line with ': ' at the start. I believe that ':' was always built in to the Unix shell, making it one of the oldest builtins along with 'chdir' (what eventually became 'cd'), although it's not mentioned in the V3 sh manpage.

(Unfortunately we don't seem to have the source for the V3 shell, and neither source nor manual page for the V2 shell. But I can't imagine early Unix not making ':' a built in command like 'chdir', and we know you could put ':' in shell scripts due to 'goto'.)

Update: To explain the connection between 'goto' and ':' a bit more, how 'goto' worked is that your script did 'goto label' and goto searched through the script for a line that said ': label' (for an arbitrary word as the label) and positioned execution there. In order to make this line valid in a shell script, there was a ':' shell builtin that did nothing.


Comments on this page:

By root at 2024-04-28 10:17:04:

you can play with the V6 shell with the tsh binary from https://etsh.nl/ (on openbsd it is in the etsh package/port). side note, V6 sh.c was renamed in V7 to osh.c for "old shell".

opening the script as stdin is in some ways cleaner, because <script sh is the same as sh script, which is not true in bash if for example script contains exec <<\. but it does break typical modern interactive wrapper scripts, but theres no reason we cant have it both ways.

interestingly it is possible to emulate the goto mechanism with ksh93 builtins:

HISTFILE=/dev/null ksh93 <<\.
echo once
#goto
echo loop
exec <#((0)) <#\#goto
.
By : root at 2024-04-30 13:43:18:

i realised it works in the bourne shell too:


. /dev/stdin << .
echo once
: loop
echo loop
goto loop
.

enjoy!

Written on 03 December 2023.
« Why Unix kernels have grown caches for directory entries ('name caches')
Getting some information about the Linux kernel dentry cache (dcache) »

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

Last modified: Sun Dec 3 21:38:17 2023
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.