I
"new" It -- A Generator for New Shell Script Templates
Joseph Pietras
I knew it! Just when everything seems to be going okay, something
else breaks. It seems like the life of a Unix systems administrator,
when not rebuilding computers, fixing the network, and putting out
fires, is spent writing scripts to make something work. In this
article, I'll describe "new", the script I use to generate a script
template.
Experience has shown that a large portion of script writing is
repetitive. Minimally, a shell needs to read the command-line options
do the work and exit cleanly. Consequently, I find that I write
the "template" part over and over. And I have to maintain multiple
templates because companies vary in the kind of scripts that they
use. Most of the companies for which I consult prefer scripts to
be written in Bourne shell (/bin/sh) since it is (in theory) more
portable. However, I prefer to write in Korn shell, because it has
so many nice language extensions, not to mention the speed improvement.
And then there's bash -- lots of companies are using it more and
more because of Linux. Of course, some sys admins require C-shell
(or TC-shell) scripts.
So, naturally, I evolved my templates for writing scripts into
a script. I wrote a shell script that writes a shell script. I guess
it's the academic in me: I love recursion. My script, "new", outputs
any one of the five templates I was maintaining -- the first versions
of "new" simply used /bin/cat, but I couldn't leave it at that.
After many iterations, I had a script that was well over 2000 lines.
Eventually, I had to break up the script because, well, the script
generator, "new", would generate a script that, when run, would
generate a script that, when run, would generate a script, that...
Anyway, I finally divided "new" into three scripts named "new",
"workctl", and "working". Now it's much easier to explain. In this
article, I will describe "new", and explain how to use it. I will
also detail the "workctl" and "working" scripts.
The "new" script will generate a template script using any of
these shells:
- Bourne shell (/bin/sh)
- Korn shell (/bin/ksh)
- Bash shell (/bin/bash)
- C-shell (/bin/csh)
- TC-shell (/bin/tcsh)
Regardless of the shell, the template script performs these steps:
1. Creates a variable NAME and dynamically binds it with the name
of the template script. If the name of the template script is changed,
the NAME variable automatically reflects the change.
2. Examines the environment variable ECHO. If the ${ECHO} is set
to any of the values true/yes/on (case insensitive), then the script
turns on debugging with "set -x" (set echo in the C-shell and TC-shell).
3. Creates a USAGE variable for I/O from the template shell. When
you run the template shell with zero command-line arguments, the
USAGE variable is printed to the screen.
4. Creates a set of uniquely named temporary files to be used
by the template as needed. These can be used to hold sed scripts,
awk scripts, temporary edits, etc.
5. Creates the template shell code needed to clean up temporary
files under common circumstances (i.e., when the template shell
receives an interrupt (SIGINT), hangup (SIGHUP), or terminate (SIGTERM)
signal). These can be easily extended. For C-shell and TC-shell,
an "onintr" logic is put into the template to handle the temporary
file cleanup chores.
6. Generates code so that the template shell will accept the "-"
option or the "--" option to override usage, thereby allowing the
programmer of the template shell to have options that enable the
user of the template shell to "take all the defaults" of the template
shell.
7. Prompts for options that the template shell program will accept.
The "new" script can create a template script with either "getopts"
or "gnu" style command-line parser logic. Alternatively, "new" can
also be used to create a template script that does not have command-line
parser logic. So, there are essentially three ways to use "new"
to create a new template script:
- With "gnu" style command-line parsing logic
- With "getopts" style command-line parsing logic
- Without command-line parsing logic
Basically, the difference between "getopts" and "gnu" style parsing
is that the traditional Unix "getopts" style only allows options
to be one character long, while the (so-called) "gnu" style parsing
allows each one-character command-line option have a "long format"
counterpart. The long format options are often easier to remember
and are very useful when used within scripts. Even if you leave
a script for a year or more, when you come back to editing it again,
you will remember what the commands in the script are doing because
of the long format options.
To make use of this feature, GNU style parsing logic is used by
"new". Consequently, users of "new" can specify command-line options
in either long format or short format.
These are the options that control the choice of parser logic
that "new" outputs to the template script:
--gnustyle | -g Enable gnu style options
--nognustyle | -n Disable gnu style options
--getopts | -G Enable getopts style options
--nogetopts | -N Disable getopts style options
--omit | -o Alias for --nognustyle and --nogetopts
When --omit has been chosen, the script writer is on his
own and will have to edit the template and write any command-line
parsing desired, otherwise "new" will prompt the user to enter the
options that the template script should recognize. Prompting is
terminated by entering an empty line or EOF (usually CTRL-D).
If "gnu" style is in effect, then "new" prompts the user twice
-- once for the long format and once for the short format of the
option. The short format defaults to the first character of the
long format, just as you would expect.
Now, when "gnu" style options are used there is a subtle issue
with regard to white space and the short format version of an option.
Here is what I mean -- if the option --output=file is valid
and -o is the short format version of this option, then -ofile
is valid. But, should -o file also be valid?
Some programmers argue that it should, and others argue that it
shouldn't. It's hard to please the customer with this kind of thing
going on. So, I solved the problem with more software. That's why
the "new" script has these command-line options:
--threevariants | -3
--twovariants | -2
The template created by "new" will or won't accept the -o file
variant of the "gnu" style short format version, depending on which
of the above options are in effect. Other options to "new" are:
--plugin | -P
--noplugin | -I
These options control whether the template script will output "SMDP_*"
variables. They are used only if the script template being generated
is going to be used with OpenSysMon (OSM) Meta Monitoring Software.
If you are interested in plug-ins for OSM, check out the Web site
(http://www.opensysmon.com).
The --date(-d) and --nodate(-D) options control
whether "new" puts a date stamp into the template that is output.
Previously, I mentioned that "new" is one of three scripts in
this set -- the other two being "workctl" and "working".
These two companion scripts exist to facilitate solving the long
response time problem. Shell script software typically runs fast,
however some shell scripts can be quite complicated and require
a very long execution time. And, when they do, they can leave the
user hanging. Any shell script that will have a long response time
can employ the "working" shell script to overcome this long response
time problem.
To effectively control the "working" script from any other script
(including script languages that do not implement functions and
traps), the programmer can use "workctl". So, depending upon the
programmer's choice of a scripting language "workctl" may or may
not be needed. However, from within "new", regardless of the kind
of template script being generated, I consistently output calls
to "workctl". I do this because it makes "new" easier to write and
makes the the generated template easier to read.
The template generated by "new" can automatically call "workctl",
which calls "working", in order to produce a template script that
is working as soon as it is created. By that, I mean it works immediately
and controls the "working" script in such a way that you, the programmer,
see what the "working" script is doing.
Options that control these scripts are:
--work | -w Enables workctl in the template
--nowork | -W Disables workctrl
For new users of "new", it is recommended that "--work" be used.
This makes the whole process easier and facilitates learning how
to use "new". Naturally, you will have to download the "working"
software also.
For example, enter these commands to build a working, advanced
script:
#edinburghBASH: ./new --work --korn --omit > template
#edinburghBASH: chmod +x ./template
#edinburghBASH: ./template -
This example assumes that you already have "workctl" and "working"
in your PATH. It also introduces the last two sets of options I will
discuss. The type of shell created by "new" is controlled by these
options:
--bourne | -B Produce a /bin/sh template
--korn | -k Produce a /bin/ksh template
--bash | -b Produce a /bin/bash template
--csh | -c Produce a /bin/csh template
--tcsh | -t Produce a /bin/tcsh template
--shell | -s Shell options are aliases for --bourne
and -B
The last options have to do with controlling where the output
of "new" goes. As this example shows, when a script template is
generated with "new", the template will be sent to standard output,
which typically is redirected. Or, if the command-line option --output=<file>
is given, the template is written to <file>.
Installation of "new" (as well as "workctl" and "working") is
trivial; you simply put the scripts in your PATH. Otherwise, they
are fully functional and do not require any configuration. However,
if you want to change any of the defaults for the scripts, you can
edit the configuration section and simply toggle the default from
TRUE to FALSE. The "new" script will then automatically adapt itself,
including its usage line, to the new defaults.
One caveat -- all three of these scripts must know the numeric
values of SIGUSR1 and SIGUSR2. Additionally, "new" needs to know
the values of SIGHUP, SIGINT, and SIGTERM. All of these numeric
values can be obtained with kill -l, or you can look in /usr/include/sys/signal.h.
The "new" software is available for download from the Sys Admin
Web site at: http://www.sysadminmag.com or from ftp://ftp.opensysmon.com/pub/new/
directory. There is a zip file along with the individual files.
Note that http://www.opensysmon.com is a shell script archive
site that also contains an open source system monitoring and network
monitoring software package. Many platforms are supplied already
compiled.
Joseph Pietras is a Sr. UNIX systems programmer living and
working in sunny San Diego, California. He holds a MS in Computer
Science and loves all things UNIX! When he's not writing code or
at the beach riding the waves he can be reached at jpietras@ChironComputing.Com.
|