Classic Shell Scripting - Arnold Robbins [57]
trap "exit 1" HUP INT PIPE QUIT TERM
trap "rm -f $PERSON $OFFICE $TELEPHONE $USER" EXIT
During development, we can just comment out the second trap, preserving temporary files for subsequent examination. (The trap command is described in Section 13.3.2. For now, it's enough to understand that when the script exits, the trap command arranges to automatically run rm with the given arguments.)
We need fields one and five repeatedly, and once we have them, we don't require the input stream from standard input again, so we begin by extracting them into a temporary file:
awk -F: '{ print $1 ":" $5 }' > $USER This reads standard input
We make the key:person pair file first, with a two-step sed program followed by a simple line sort; the sort command is discussed in detail in Section 4.1.
sed -e 's=/.*= =' \
-e 's=^\([^:]*\):\(.*\) \([^ ]*\)=\1:\3, \2=' <$USER | sort >$PERSON
The script uses = as the separator character for sed's s command, since both slashes and colons appear in the data. The first edit strips everything from the first slash to the end of the line, reducing a line like this:
jones:Adrian W. Jones/OSD211/555-0123 Input line
to this:
jones:Adrian W. Jones Result of first edit
The second edit is more complex, matching three subpatterns in the record. The first part, ^\([^:]*\), matches the username field (e.g., jones). The second part, \(.*\) ❒ , matches text up to a space (e.g., Adrian ❒ W.❒ ; the ❒ stands for a space character). The last part, \([^ ❒ ]*\), matches the remaining nonspace text in the record (e.g., Jones). The replacement text reorders the matches, producing something like Jones,❒ Adrian W. The result of this single sed command is the desired reordering:
jones:Jones, Adrian W. Printed result of second edit
Next, we make the key:office pair file:
sed -e 's=^\([^:]*\):[^/]*/\([^/]*\)/.*$=\1:\2=' < $USER | sort > $OFFICE
The result is a list of users and offices:
jones:OSD211
The key:telephone pair file creation is similar: we just need to adjust the match pattern:
sed -e 's=^\([^:]*\):[^/]*/[^/]*/\([^/]*\)=\1:\2=' < $USER | sort > $TELEPHONE
At this stage, we have three separate files, each of which is sorted. Each file consists of the key (the username), a colon, and the particular data (personal name, office, telephone number). The $PERSON file's contents look like this:
ben:Franklin, Ben
betsy:Ross, Betsy
...
The $OFFICE file has username and office data:
ben:OSD212
betsy:BMD17
...
The $TELEPHONE file records usernames and telephone numbers:
ben:555-0022
betsy:555-0033
...
By default, join outputs the common key, then the remaining fields of the line from the first file, followed by the remaining fields of the line from the second line. The common key defaults to the first field, but that can be changed by a command-line option: we don't need that feature here. Normally, spaces separate fields for join, but we can change the separator with its -t option: we use it as -t:.
The join operations are done with a five-stage pipeline, as follows:
Combine the personal information and the office location:join -t: $PERSON $OFFICE | ...
The results of this operation, which become the input to the next stage, look like this:ben:Franklin, Ben:OSD212
betsy:Ross, Betsy:BMD17
...
Add the telephone number:... | join -t: - $TELEPHONE | ...
The results of this operation, which become the input to the next stage, look like this:ben:Franklin, Ben:OSD212:555-0022
betsy:Ross, Betsy:BMD17:555-0033
...
Remove the key (which is the first field), since it's no longer needed. This is most easily done with cut and a range that says "use fields two through the end," like so:... | cut -d: -f 2- | ...
The results of this operation, which become the input to the next stage, look like this:Franklin, Ben:OSD212:555-0022
Ross, Betsy:BMD17:555-0033
...
Re-sort the data. The data was previously sorted by login name, but now things need to be