The Unix Programming Environment (1984): trying an example on Linux

Good old fashioned book learning

I don't know what happened to my original copies of the two books we use to use as Unix bibles: Kernighan and Ritchie's "The C Programming Language" and Kernighan and Pike's "The Unix Programming Environment". Lost in a house move maybe. We often talk of one mythical box that contains everything we can no longer find but disappeared during a move, and they are probably in that box.

I did buy a copy of "The Unix Programming Environment" from a local second hand bookstore some time ago, it's entertaining, informative and has a brief overview of everything from file system fun to C programming and troff. It even has a section on using the "ed" editor, which isn't installed by default on anything anymore (sudo apt-get install ed fixes that). Mostly I use it to refresh those shell scripting skills I used to have but have atrophied over years of using Windows. However it seems some of the bourne shell examples don't seem to work on Linux, not because bash isn't great, but because the linux executables sometimes don't behave like they used to 30 years ago. Today's example: date(1).

GNU date or BSD date

The first example in the shell programming chapter in the book has a small wrapper for "cal(1)" that allows you to type in a calendar month as a word, rather than the month number. It's a fine little example, using case statements, the "set" shell built-in, command line arguments etc. But if you type it in as described in the book, it doesn't work.

The problem is trivial, date(1) when run as is used to put the year as the 6th output, but the version on this machine (Debian 11) puts the year as the 4th output. It's probably a GNU date thing vs a BSD date thing. Fixing the script isn't hard (original found on page 135, chapter 5 of the 1984 edition of the book)

#!/bin/sh

# cal2: nicer interface to /usr/bin/cal

case $# in
0) set `date`; m=$2; y=$4 ;; # no args: use today
1) m=$1 set `date`; y=$4 ;; # 1 arg: use this year
2) m=$1; y=$2 ;;
esac

case $m in
jan*|Jan*) m=1 ;;
feb*|Feb*) m=2 ;;
mar*|Mar*) m=3 ;;
apr*|Apr*) m=4 ;;
may*|May*) m=5 ;;
jun*|Jun*) m=6 ;;
jul*|Jul*) m=7 ;;
aug*|Aug*) m=8 ;;
sep*|Sep*) m=9 ;;
oct*|Oct*) m=10 ;;
nov*|Nov*) m=11 ;;
dec*|Dec*) m=12 ;;
[1-9]|10|11|12) ;;  # numeric month
*)          y=$m; m="" ;; # plain year
esac

/usr/bin/cal $m $y                 # run the real one

(note - modified from the original that had "y=$6" as the means of grabbing the year from the date(1) command)

I don't mind this copy of the book is out of date or that Linux/GNU isn't really the BSD that Kernighan and Pike used, I think you learn more from having to fix a broken example anyway. If you come across a copy of this book, It's as useful today as it was when my first boss shoved it in my hands and sent me off to a vt220 hooked up to a Pyramid to start my first day on the job. Next job is to find a proper 1980s copy of the C book.

Popular posts from this blog

Tailscale ate my network (and I love it)

That magical word: Workstation

Time machine: Solaris 2.6 on QEMU