Classic Shell Scripting - Arnold Robbins [90]
To do this, we need to identify users by login shell and create a mailing list that the installer can use when preparing mail announcing the new shell version. Since the text of that message is likely to differ at each announcement, we won't make a script to send mail directly, but instead, we just want to make a list that we can mail to. Mailing-list formats differ among mail clients, so we make the reasonable assumption that ours only expects a comma-separated list of email addresses, one or more per line, and does not mind if the last address is followed by a comma.
In this case, a reasonable approach is to make one pass through the password file, creating one output file for each login shell, with one comma-terminated username per line. Here is the password file that we used in Chapter 5:
jones:*:32713:899:Adrian W. Jones/OSD211/555-0123:/home/jones:/bin/ksh
dorothy:*:123:30:Dorothy Gale/KNS321/555-0044:/home/dorothy:/bin/bash
toto:*:1027:18:Toto Gale/KNS322/555-0045:/home/toto:/bin/tcsh
ben:*:301:10:Ben Franklin/OSD212/555-0022:/home/ben:/bin/bash
jhancock:*:1457:57:John Hancock/SIG435/555-0099:/home/jhancock:/bin/bash
betsy:*:110:20:Betsy Ross/BMD17/555-0033:/home/betsy:/bin/ksh
tj:*:60:33:Thomas Jefferson/BMD19/555-0095:/home/tj:/bin/bash
george:*:692:42:George Washington/BST999/555-0001:/home/george:/bin/tcsh
The script itself combines variable and command substitution, the read command, and a while loop to get everything done in less than ten lines of executable code! See Example 7-3.
Example 7-3. Convert password file to shell mailing list
#! /bin/sh
# passwd-to-mailing-list
#
# Generate a mailing list of all users of a particular shell.
#
# Usage:
# passwd-to-mailing-list < /etc/passwd
# ypcat passwd | passwd-to-mailing-list
# niscat passwd.org_dir | passwd-to-mailing-list
# Possibly a bit of overkill:
rm -f /tmp/*.mailing-list
# Read from standard input
while IFS=: read user passwd uid gid name home shell
do
shell=${shell:-/bin/sh} # Empty shell field means /bin/sh
file="/tmp/$(echo $shell | sed -e 's;^/;;' -e 's;/;-;g').mailing-list"
echo $user, >> $file
done
As each password file entry is read, the program generates the filename on the fly, based on the shell's filename. The sed command removes the leading / character, and changes each subsequent / to a hyphen. This creates filenames of the form /tmp/bin-bash.mailing-list. Each user's name and a trailing comma are then appended to the particular file, using >>. After running our script, we have the following results:
$ cat /tmp/bin-bash.mailing-list
dorothy,
ben,
jhancock,
tj,
$ cat /tmp/bin-tcsh.mailing-list
toto,
george,
$ cat /tmp/bin-ksh.mailing-list
jones,
betsy,
Being able to create mailing lists can be generally useful. For example, if process accounting is enabled, it is easy to make a mailing list for every program on the system by extracting program names and the names of the users who ran the program from the process accounting records. Note that root privileges are required to access the accounting files. Accounting software varies from vendor to vendor, but the same sort of data is accumulated by all of them, so only minor tweaks should be necessary to accommodate their differences. The GNU accounting summary utility, sa (see the manual pages for sa(8)), can produce a report with output lines that look like this:
# sa -u
...
jones 0.01 cpu 377k mem 0 io gcc
...
That is, we have whitespace-separated fields in which the first entry is a username and the last is a program name. This suggests that we simply filter that output to make it look like password-file data, and then pipe it into our mailing-list program:
sa -u | awk '{ print $1 "::::::" $8 }' | sort -u | passwd-to-mailing-list
(The sort command sorts the data; the -u option removes duplicate lines.) The beauty of Unix filters and pipelines, and simple data markup, is readily apparent. We don't have to write a new mailing-list creation program to handle accounting