home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.robelle3000.ai 2014
/
2014.06.ftp.robelle3000.ai.tar
/
ftp.robelle3000.ai
/
papers
/
progci.pro
< prev
next >
Wrap
Text File
|
1995-09-10
|
54KB
|
1,422 lines
.com.sel(1)
.com {ifout starts here}
.comment Choose the output device parameters for this document
.if outfinal
. if outrecord
. out(las 30 c1 r+ s4).com Robelle bound version, attached
. else
. out(las 30 c1 r- s4).com Robelle bound version
. endif
.elseif outhelpcomp
. out(lpt q+ w80).com helpcomp;parm=1
.elseif outa4
. if outlpt
. if outrecord
. out(lpt s4 r+).com A4 paper, $Stdlist/LP/disc, attached
. else
. out(lpt s4 r-).com A4 paper, $Stdlist/LP/disc
. endif
. elseif outlaser
. if outrecord
. if outdouble
. out(las 30 s5 r+ d+).com A4 paper, LaserJet, attached, duplex
. else
. out(las 30 s5 r+).com A4 paper, LaserJet, attached
. endif
. else
. if outdouble
. out(las 30 s5 r- d+).com A4 paper, LaserJet, duplex
. else
. out(las 30 s5 r-).com A4 paper, LaserJet
. endif
. endif
. else.com No other outxxx jcws specified
. out(lpt s4 r-).com generic: A4 paper, $Stdlist/LP/disc
. endif
.else
. if outlpt
. if outrecord
. out(lpt s3 r+).com Letter, $Stdlist/LP/disc, attached
. else
. out(lpt s3 r-).com Letter, $Stdlist/LP/disc
. endif
. elseif outlaser
. if outrecord
. if outdouble
. out(las 30 s4 r+ d+).com Letter, LaserJet, attached, duplex
. else
. out(las 30 s4 r+).com Letter, LaserJet, attached
. endif
. else
. if outdouble
. out(las 30 s4 r- d+).com Letter, LaserJet, duplex
. else
. out(las 30 s4 r-).com Letter, LaserJet
. endif
. endif
. else.com No outxxx jcws specified
. out(lpt s3 r-).com Letter, generic: $Stdlist/LP/disc
. endif
.endif
.comment
.include f92286f.qlibdata.robelle
.include styles.qlibdata.robelle
.form(k1 [ ///l51 / #31"Programming the CI"/#34"4018-"Pn:0////])
.form(k2 [ ////l51 //// ])
.form 2
.if not outhelpcomp
. mar(k1 l0)
. mar(k2 l0)
.endif
.opt (f- l- r-)
.title 4018
.com |6PAPER NUMBER 4018|
.font 3
Programming the Command Interpreter
|1An Introduction, a Dog, and New Tricks|
.font 1
by Ken Robertson
July 1995
Robelle Consulting Ltd.
Unit 201, 15399-102A Ave.
Surrey, B.C.@@Canada V3R 7K1
Toll-free:@@1-800-561-8311
Phone:@@(604) 582-1700
Fax:@@(604) 582-1799
E-mail:@@ken_robertson&@robelle.com
WWW:@@http://www.robelle.com
.font 0
Copyright Robelle Consulting Ltd. 1995
Permission is granted to reprint this document (but ~not~
for profit), provided that copyright notice is given.
.opt
.font 0
.count 0
.form 1
.page
.opt (l- r- f-)
|3 Programming the Command Interpreter|
|1An Introduction, a Dog, and New Tricks|
|1by Ken Robertson|
.opt
|3Introduction|
An overworked, understaffed data processing department is all too common
in today's ever belt-tightening, downsizing and de-staffing
companies.
An ad-hoc request may come to the harried data processing manager. She may
throw her hands up in despair and say, "It can't be done. Not within the time
frame that you need it in." Of course, every computer-literate person knows
deep down in his heart that every programming request can be fulfilled, if the programmer
has enough
hours to code, debug, test, document and implement the new program. The informed DP manager
knows that programming the Command Interpreter (CI) can sometimes reduce that
time, changing the "impossible deadline" into something more
achievable.
In this paper you will learn some of the programming concepts
of the CI. You will also learn how you can use the CI to solve some common
data processing problems.
You will find a short glossary at the end that explains
some of the terms used in this paper.
|3A Little History|
Before MPE/iX, (and even before MPE/XL), there was a run-time
environment for the MPE/V class of HP computers called the Command
Interpreter (CI).
The Command Interpreter on MPE/V machines had limited programming capability,
with If@/@@Else constructs and numeric variables limited to values between
0 and 65535. The basic interface of the MPE/V CI
was ported to MPE/iX machines, and beefed up to be usable as a
run-time shell.
The MPE/iX Command Interpreter has a generous
command set that pushes the shell into the realm of a true programming
tool. Its ability to evaluate expressions, and to perform I/O on
files allows the end user to perform simple data processing
functions without having to resort to using a 3GL.
The CI can be used to solve complex problems.
Since its code is interpreted, a CI solution to a problem may execute too
slowly for practical purposes. In this case, a 3GL, 4GL, or third- party software solution would
have to be investigated.
|3What are Command Files?|
Command files are a collection of commands in flat files, of either variable or
fixed length record structure, that reside in the MPE or POSIX
file space. Phew! Basically, command files are what you could call MPE macros.
Anything that you can do interactively in the CI, you can do with command files,
and then some.
You can use command files in situations that call for
repetitive functions, such as recompiling source code, special spooler commands,
etc. Command files are also great
when you want to hide details from the
end user.
A command file is executed when its name is
typed in the CI, or
invoked from a command file or programming shell. Just as in
program execution, the user's Hppath is searched to determine the location of
the command file.
When you're deciding where to store a command file,
it's a good idea to have some conventions in mind.
One common convention involves creating
a CMD group (short for command group) in every account for local command files, and a
production CMD group that everyone can access. You can set up a logon UDC to
ensure that the Hppath variable is set correctly.
Hey - why not have a logon command file? Well, you can't, because the current
version of MPE doesn't support it.
If you insist on having one, you can always make a logon UDC that executes
your logon command file.
.style example
***********************
logonudc
option logon, nobreak
logon.cmd.prod
bye
.style body
The above UDC is invoked at logon, and executes the command file
called |5logon.cmd.prod|. The last line in the command file logs the user off.
One question that often arises is "Which are better - command files or UDC's?"
Interestingly enough, UDC stands for User Defined Command. UDC's must be managed
differently from command files, and have small differences in their internal organization.
A UDC file is a library of callable commands with multiple entry points, whereas a command file
has but one entry point. You manage UDC files via the Setcatalog and Showcatalog commands. If
you need to make
modifications to a UDC, you must make sure that no
users are accessing the file when you want to save your changes.
UDC files must be attached to a session using the Setcatalog command. This means that the
UDC file (and all of the UDC's you set up) will be in use while a user is logged on.
In order to make changes to a UDC file, you will have to either bump the users accessing the file off
the system, or create a brand new UDC file and issue a new Setcatalog command. The changes that you made
will take effect only when the user logs back on again.
With command files, on the other hand,
you may create new ones at any time. They will be immediately accessible to anyone who is
logged on.
You still need exclusive access to delete or to make changes to an existing command file,
but users are also less likely to be accessing command files.
In the current operating system, UDC's do tend to be more secure - you can use |5option nohelp| to prevent snooping
eyes from reading the contents of a particular user command. (Note: A future operating system release
will allow execute access for command files. Currently, you must have execute and read access to the files in order
to use them.)
.page 12
|3The Command File Structure|
The structure of a command file is as follows:
.style example
PARM parameter declarations (optional)
MPE command
.
.
.
MPE command
.style body
Any MPE command can be used. This includes If@/@@Else,
and While to form looping constructs.
You can also invoke any command file or UDC by its name. Command files
can be called recursively, nesting levels up to 30
times deep. |2(Note to Qedit users: Qedit has a maximum recursive level
of 10, which should be enough.)|
.page 7
|3The Programming Environment|
Programmers will tell you that they need an "environment in which to
operate". Development of code should progress smoothly, with a minimum of
effort required to translate ideas into working programs.
For this you need an editor, and in the case of 3GL and 4GL, a program
(compiler) to compile your code. Strictly speaking, you don't need an editor to
create CI "programs", but it does make your job of writing the code a lot easier.
Qedit, from Robelle Consulting, is a flexible editor in which to develop CI
programs. You can stay inside Qedit to write, to keep and to test your command
files, which can save you steps in the traditional
"Text, Keep, Exit,
Try Again, and Edit" cycle.
If you don't want to use an editor to create command files, you can use a
feature of the CI called I/O redirection. For example, the following CI commands
will build a simple command file called |5sj|.
.font 5
echo echo All executing jobs: > sj
echo showjob;exec >> sj
.font 0
Note that as your command files become more complex, you may find it trickier
to edit them in this manner.
Consider a command file that can be used
to look up telephone numbers from a flat file:
.style example
anyparm search_string = "?"
if "!search_string" = "?" then
echo command file: phone search_string
return
endif
grep.hpbin.sys "-i '!search_string' PHONES"
.style body
How can you echo variables into a file without getting the assigned value of the variable?
Say, for example, the value of the variable |5my_time| was equal to "10:02".
The statement,
.style example
echo echo The time is !mytime > cmdfile
.style body
would put the string "echo The time is 10:02" into the first record of the file.
To get the variable name into the file, you must use two exclamation marks at
the beginning of the variable name, like this:
.style example
echo echo The time is !!mytime > cmdfile
.style body
That would write the string "echo The time is !my_time" into the first
record of Cmdfile.
.page 8
|3Variables|
As in any program, you use variables to keep track of things. HP
provides three types of variables in the CI: numeric, boolean
and string. Numerics are limited to integers, booleans to true or false,
and strings to lengths up to 255 characters.
Unfortunately, there is no provision for arrays. However, if you really
need them, they can be simulated (slowly!) using either files or
strings, or translatable naming conventions. We'll discuss this
a bit later.
The variable name itself can be a combination of alphanumeric characters
and the underbar character, but the name must start with either a
letter or an underbar.
.page 7
|3Setting and Displaying Variables|
You have two methods for setting variables: use the typical
assignment command, or
prompt a value from a terminal.
.font 5.opt (f-)
1. setvar |2variable_name| |2expression|
2. input |2variable_name| |2prompt|
.font 0
.opt
The expressions for Setvar can be made up of either numerics, strings,
booleans, variable names, or variable dereferencing (sort of like pointers),
all glued together with operators. For example, to concatenate the values of two
string variables, you can do the following:
.font 5
setvar group_account "!hpgroup"@+@"."@+@"!hpaccount"
.font 0
If the logon group and account were Pub and Sys, the above would set the variable
|5group_account| to the value of Pub.Sys.
Figure 1 shows a list of all of the possible operators in a Setvar command.
.page 12
.font 6
.box(h9 w21).box(r+1 h1 w55).box(r+2 w55).box(r+3 w55)
.box(r+4 w55).box(r+5 h2 w55).box(r+7 h1 w55)
.box(h9 w55)
.opt (f-).mar(r+12)
#+2Logical operators: #+23AND, OR, XOR, NOT
#+2Boolean functions and values: #+23BOUND, TRUE, FALSE, ALPHA, ALPHANUM, NUMERIC, ODD
#+2Comparison operators: #+23=, <>, <, >, <=, >=
#+2Bit manipulation operators: #+23LSL, LSR, CSR, CSL, BAND, BOR, BXOR, BNOT
#+2Arithmetic operators: #+23MOD, ABS, * , / , + , -, ^ (exponentiation)
#+2Functions returning strings: #+23CHR, DWNS, UPS, HEX, OCTAL, INPUT, LFT, RHT, RPT,
#+2 #+23LTRIM, RTRIM, STR
#+2Functions returning integers: #+23ABS, LEN, MAX, MIN, ORD, POS, TYPEOF
#+2Other functions: #+23FINFO, SETVAR
#+2
.font 0
.opt.opt(l- r-).mar
|2Figure 1. Setvar Operators|
.opt.font 0
.style body
.page 4
|3Variable Locality and Longevity|
Although some variables seem permanent, only a few
system-supplied, read-only variables are permanent. For example, the HPSUSAN variable
contains the special CPU number unique to each HP 3000/iX computer.
All HP-defined variables start with the letters HP, and appear
in upper case. To see a list of them, simply enter this command,
.style example
showvar hp&@
.style body
User variables@-@the variables that you create@-@can have
a lifetime as long as your session, but no longer.
You create variables using the Setvar command, and delete
them using the Deletevar command.
CI variables are global to your session. If you create a variable and then start up a new CI
shell, the variables will be accessible from the new shell as well. You do have
the ability to create local variables which exist only during
the execution of a command file. You define local variables with the
Parm line in the command file. For example,
.style example
parm local_myvar="Not Available In Stores"
echo !local_myvar
.style body
If after executing the above command file, you do a |5showvar local_myvar|, the variable will
not exist, and a CI error will grace your screen.
The temporary variable |5my_var| existed only during the execution of the command file.
Local variables cannot be modified, and are only accessed by reference
within the command files. If you have a local variable and a permanent variable
with the same name, then the local variable takes precedence during expansion.
Make sure that
your local variables have different names from your permanent ones, otherwise you'll have
more confusion than you need at debugging time.
The following command file illustrates local versus permanent variable referencing.
.style example
|1hithere|
parm hpjobname="ZZTOP"
echo Good evening, !hpjobname.
showvar hpjobjname
Executing Hithere gives us:
Good evening, ZZTOP
HPJOBNAME = KEN
.style body
Attention System Managers: Don't worry!@
This trick won't fool programs that retrieve the value of variables using the HPCIGETVAR
intrinsic, or command files that are nested within a command file. The value of
the local variable will be returned during expansion |1only| within the command file
that defines it.
.page 10
|3Getting Data Into and Out of Files|
So you want to keep some data around for awhile? Use a file! Well, you knew
that already, I'll bet. What you probably didn't know is that you can get data into and
out of files fairly easily using I/O redirection and the print command.
I/O redirection allows input or output to be directed to a file instead of to
your terminal.
I/O redirection uses the symbols ">", ">>" and "<". Use ">" to redirect
output to a temporary file (you can make the file permanent if you use
a file command);
use ">>" to append output to the file; finally, use "<"
to re-direct input from a file.
.style example
echo Value 96 > myfile
echo This is the second line >> myfile
input my_var < myfile
setvar mynum_var str("!my_var",7,2)
setvar mynum_var_2 !mynum_var - (6 * 9 )
echo The answer to the meaning of life, the universe
echo and everything is !mynum_var_2.
.style body
After executing the above command file, the file Myfile will contain two lines,
"Value 96" and "This is the second line" (without quotes, of course). The Input
command uses I/O redirection to read the first record of the file, and assigns
the value to the variable |5my_var|.
The first Setvar
extracts the number from the middle of the string, and proceeds to use the value
in an important calculation in the next line.
How can you assign the data in the second and subsequent lines of a file to
variables? You use the Print command to select the record that you want from
the file, and send the output to a new file.
.style example
print myfile;start=2;end=2 > myfile2
.style body
You can then use the Input command to extract the string from the second
file.
.page 4
|3Rolling Your Own System Variables|
It's easy enough to create a static file of Setvar commands
that gets invoked at logon time, and it's not difficult to modify
the file programmatically. For example, let's say that you would like
to remember a particular variable from session to session, such
as the name of your favorite printer. You can name
the file that contains the Setvars, Mygvars. It will contain
the line:
.style example
setvar my_printer "biglaser"
.style body
The value of this variable may change during your session, but you may want
to keep it for the next time that you log on. To do this,
you must replace your normal logoff procedure (the Bye or Exit command) with
a command file that saves the variable in a file, and then
logs you off.
.style example
|1byebye|
purge mygvars > $null
file mygvars;save
echo setvar my_printer "!my_printer" > *mygvars
bye
.style body
Whenever you type |5byebye|, the Setvar command is written to Mygvars,
and you are then logged off. The default close disposition of an I/O redirection
file is TEMP, which is why you have to specify a file equation. Because you are never
certain that this file exists beforehand, doing a Purge ensures that it does not.
.page 3
|3Program Control - If/Else and While|
One of the reasons I like programming the CI is its lack of
|2goto's|. This much-debated programming style has sparked many a heated discussion.
Consider the following:
.style example
echo Powers of 2...
if "!hpjobname" = "KARNAK" then
setvar loop_count 1
setvar temp 1
while !loop_count < 10 do
setvar temp !temp * 2
echo 2^!loop_count = !temp
setvar loop_count !loop_count + 1
endwhile
else
echo Sorry, you're not authorized to know.
endif
.style body
The above command file will display a powers of two table from one though
nine for the user who is logged on as KARNAK. It is good programming
practice to indent code inside If and While blocks, because it makes debugging
much easier. The CI doesn't care about leading spaces or blank lines,
but you must use the Comment command to insert comments.
There are times when you wish to get out of a loop in an ungraceful
manner. To do this, use the Return command. I often use this
when I display help in a command file, and I don't want to clutter
further code with a big If@/@@Endif block.
.style example
parm hidden_away="?"
if "!hidden_away" = "?" then
echo This command file doesn't do much,
echo but it does it well.
return
endif
echo Local variable is !hidden_away.
.style body
Another way to terminate loops and nested command files is to use
the Escape command. This command will blow you right back to
the CI. (Using the Return command only exits the current command file.) You can optionally
set the CIERROR JCW by adding a value to the end of the Escape command.
.style example
escape 007
.style body
.page 5
|3Simulating Arrays|
It's true - arrays are not directly supported in the CI. However,
because of some undocumented techniques (read |2tricks|), you can simulate
arrays.
One question that may immediately pop into your head is "Why would I want
to use arrays?" Arrays
are useful for table driven events, such as returning days per month, sessions on
ldevs, etc.
.page 5
I won't keep you in suspense. Here's the core method:
.style example
setvar !variable_name!variable_index |2value|
.style body
By using the expression evaluation feature of the CI, you
can have a changeable variable name in the Setvar command.
|1CAVEAT USER:| This only works within command files!| If you try
to do this
interactively, the expression evaluation on the Setvar command is
performed for the part of the command line |2after| the variable
name. Within a command file, the entire line is
evaluated before being passed to the CI for re-evaluation.
In much the same way, you can use command files to change sequences of
commands, such as creating self-modifying code. For example,
.style example
|1weirdcmd|
setvar variable_command "setvar apple ""orange"""
!variable_command
.style body
If you run
the command file, and then display the contents of the variable, you will see:
.style example
weirdcmd |0{execute command file, above}|
showvar apple
|APPLE = orange|
.style body
To simulate arrays, you must assign one variable for each element.
For example, you would assign months_12 for months(12).
This variable can be
either string or numeric, but keep in mind that string variables can be up to
256 characters long.
Here are a few command files that allow you to manipulate arrays.
.style example
|1arraydef|
parm array_name nbr_elements=0 initial_value=0
setvar array_index 0
while !array_index <= !nbr_elements do
setvar !array_name!array_index !initial_value
setvar array_index array_index + 1
endwhile
.style body
The command file Arraydef allocates variables for each element of the
array that you need. The call sequence would be something like,
.style example
arraydef months_ 12
.style body
Just as you put
an index in parentheses, I like to put underbars at the
end of array names so that they are more readable.
You can
use this command file to ensure that you have enough room in the
CI data area to store
your array. Under MPE/iX release 5.0, space for variables is
limited to roughly 20K. The space used can be calculated by adding the number of characters
in each variable name, plus 4 bytes per integer variable
or the length of each string variable. For example, the integer
variable "XYZZY" takes up 5 bytes for the name, and four more
bytes for its integer value. When you run out of space, you
get the following error from the CI:
.style example
Symbol table full: addition failed. To continue, delete
some variables, or start a new session. (CIERR 8122)
.style body
At the 1995 IPROF conference, I asked the MPE roundtable how much
symbol space was actually available, but no one could give a definitive answer.
As a question to my question, the roundtable countered with "How many people have ever run
out of symbol space?" Only one person put up his hand in a room of 200 or so programmers - me.
Just wait until more people start using arrays.
The following command file allows you to return the space used
by your pseudo-array when you are finished with it.
.style example
|1arraydel
parm array_name nbr_elements=0
setvar array_index 0
while !array_index < !nbr_elements do
deletevar !array_name!array_index
setvar array_index array_index + 1
endwhile
.style body
To demonstrate how arrays can be set (and values returned), the following two
command files, Arrayset and Arrayget, use the expression evaluation
feature of the CI to convert the element number to the actual variable name.
Setvar is called to set either the value, or the name of the variable
passed to the command file.
.style example
|1arrayset|
parm array_name element_nbr=0
anyparm set_value
setvar !array_name!element_nbr !setvalue
.page 4
|1arrayget|
parm destination_var array_name element_nbr=0
anyparm get_value
setvar !destination_var !array_name!element_nbr
.style body
Here's a quick command file to show how you can use arrays in a month
table. It uses the Input command to prompt the user for the
number of days for each month.
.style example
setvar months_nbr 0
while !months_nbr < 12 do
setvar months_nbr months_nbr + 1
input months_!months_nbr; &&
prompt="Enter the number of days in month
!months_nbr: "
endwhile
deletevar months_nbr
.style body
.page 5
|3Calling All Functions!|
The CI has a number of built-in functions that you can call
from the CI commands
If and Setvar. The purpose of these functions
is to provide some higher programming features at the CI level.
For example, when you accept user input, you may want to upshift
the string to minimize your If logic.
.style example
input yes_no,"Please enter yes or no: "
if ups("!yes_no") = "YES" then
.style body
Since people generally like to reply with one-character
answers, you can add one more function to your command file.
.style example
input yes_no,"Please enter yes or no: "
if lft(ups("!yes_no"),1) = "Y" then
.style body
In the above two examples, the |5ups()| function upshifts the argument
string. |5lft()| returns the number of specified characters.
The characters start at the leftmost portion of the string argument. If you
look closely, you'll notice that there are no quotes around the
inside function call, that is you do |1not| say,
.style example
if lft("ups("!yes_no")",1) = "Y" then
.style body
Because of CI expansion rules, you cannot use quotes when you embed a function within a function.
If you think about it, the |5ups()| function
in the above statement would never get resolved. If you play computer and expand the above
statement, you get the following:
.style example
IF lft("ups("yes")",1) = "Y" THEN
.style body
which, when executed, produces the error, |5A string operator was expected but none was found. (CIERR 9755)|
.page 8
Here are a couple of useful functions. You'll find the rest (as of MPE/iX 4.5)
described at the end of this paper, or you can check Appendix B
in Volume 2 of the HP's |2MPE/iX Commands Reference Manual|.
.style example
dwns(@) #15Shift string to lowercase
finfo(@) #15Returns file info, such as eof, etc.
rht(@) #15Returns rightmost count of characters
str(@) #15Extract a portion from the middle of a string
.style body
.page 5
|3Popular Command Files|
To finish off this paper, I've included
some sample command files that not only
illustrate previously discussed techniques, but also include a few new
tricks.
.page 4
|3Obtaining the Remote CIERROR|
If you have multiple HP 3000's linked with NS services, then you will
know exactly why you need this functionality. You cannot get the remote value of
the CIERROR JCW using traditional methods.
More importantly, you cannot easily detect when a remote command fails.
For example, the following commands inside a job will |1not| work as you expect:
.style example
remote purge bigfile
if cierror <> 0 then
echo error!
endif
.style body
In fact, the HP Commands reference manual states:
.mar (l+4 r-4)
.font 0
When used to invoke commands on remote systems the COMMAND or HPCICOMMAND
intrinsics do not return a meaningful status code. For more information
on calling intrinsics refer to the MPE/iX Intrinsics Reference Manual
(32650-90028).
.font 0
.mar
.style body
Clearly you must do a bit more work to get your desired results.
I've created two command files that you will need called
Remci.Cmd.Prod and Echoci.Cmd.Prod.
.page 5
.style example
|1remci.cmd.prod|
remote echoci tempci
remote purge tempci > $null
remote save tempci
dscopy tempci:remmach;tempci;rep
continue
tempci
|1echoci.cmd.prod
parm tempfile
echo setjcw cierror !cierror > !tempfile
.style body
You will need to keep the Remci command file on the local machine, and the
Echoci command file on the remote one. The first thing the
Remci command file does is remotely invoke the Echoci command
file. This inserts the line |5setjcw cierror [cierror value]| into
the temporary file Tempci. Remci then pulls the
file over to the local machine via Dscopy, and executes the file,
setting the current |5cierror| to the value of the remote |5cierror|.
You can now insert this command file call into your first jobstream
and make it work.
.style example
remote purge bigfile
remci
if cierror <> 0 then
echo error purging remote bigfile!
endif
.style body
.page 4
|3Job Monitoring|
In a perfect world, jobs would never fail, and no one
would never mistype a job number when aborting a job. Since the world is
far from perfect, it's nice to have something that helps
monitor critical jobs.
.style example
|1checkjob|
anyparm jobname
purge sojfile >$null
purge sojfile,temp >$null
purge grepout,temp >$null
purge grepout > $null
showjob job=&@j;exec >sojfile
save sojfile
run grep.hpbin.sys;info="-ci '!jobname' SOJFILE" > grepout
save grepout
input nbr_found < grepout
if !nbr_found = 0 then
echo [esc]&dB!jobname is not running.
setvar job_is_running FALSE
else
setvar job_is_running TRUE
echo !jobname is in exec status.
endif
.style body
A gotcha about |5grep|: |5grep| will only read |2permanent| files, and
it doesn't pay attention to file equations.
This powerful tool ported from UNIX is
used to quickly find occurrences of strings within a file. The |2-c| option used
above tells |5grep| to return only the count of strings found, while the |2i| indicates that
the search shouldn't care about upper or lower case, (|1I|gnore case,
I guess.) In our command file, the search file is in the MPE name
space, and must appear in UPPER CASE.
|3Displaying Active Jobs|
At Robelle, our shop occasionally has two to three hundred jobs in a scheduled state.
Typing Showjob produces pages of data that one doesn't necessarily wish
to view. The following command file called Sja will show only the
jobs that are in the EXEC state, and some statistics about jobs in
other states.
.page 5
.style example
|1sja|
echo JOBNUM STATE IPRI JIN JLIST INTRODUCED
JOB NAME&&[esc]&&a65C[esc]&&dJ!hpsysname
purge sojfile,temp >$null
showjob job=&@j;exec >sojfile
setvar lastline finfo("sojfile","eof") - 4
print sojfile;start=4;end=!lastline
if !hpjoblimit < !hpjobcount then
echo [esc]&&dA************ W A R N I N G ***********
echo [esc]&&dBNumber of jobs executing exceeds
JOBLIMIT
echo [esc]&&dA**************************************
elseif (!hpjobcount + 1) < !hpjoblimit then
echo [esc]&&dA******* W A R N I N G ****************
echo [esc]&&dB Joblimit will allow two jobs to run
echo [esc]&&dB at the same time
echo [esc]&&dA**************************************
elseif !hpjoblimit = !hpjobcount then
echo [esc]&&dH** WARNING: The JLIMIT queue is full.
endif
echo &&
[esc]&&dJ Sessions: !hpsescount &&
[esc]&&dBJobs EXEC: !hpjobcount (limit !hpjoblimit) &&
[esc]&&dB WAIT: !hpwaitjobs &&
[esc]&&dJ SCHED: !hpschedjobs [esc]&&d@&&
[esc]&&dJ SUSP: !hpsuspjobs
.style body
The above command file doesn't have any sneaky tricks, but it does use
I/O redirection
and a file function call
to determine the number of lines in the output of the Showjob;exec
command. If you subtract four from the EOF, you get the number of jobs
executing.
Note that |5[esc]| indicates the escape character, ASCII 27. That is, |5[esc]&&dB|
is the escape sequence to turn on inverse video.
|3Creating a Job History|
.com new KR
Have you ever streamed a job, cleared your screen, then wished you could
remember the job number that MPE assigned the stream? The following
command file, Jstream, takes care of this requirement nicely. It streams the job passed to it, and
maintains a history of stream events. It
creates a new variable called |5jobhist_|, where is a unique
job number. The jobname, current time, and job number are kept
in this variable. The command file Jobhist provides a simple
history viewer.
.com endnew
.style example
.page
|1jstream|
anyparm stream_jobname = ?
echo JSTREAM 1.2 Type JOBHIST to see your jobstream history.
if finfo("!stream_jobname","exists") = FALSE then
echo Yowza! I can't access !stream_jobname
escape 52
return
endif
setjcw cierror 0
continue
stream !stream_jobname > str52673
setvar temp_cierror !cierror
if temp_cierror <> 0 then
print str52673
endif
input stream_out < str52673
purge str52673,temp > $null
if lft(stream_out, 3) <> " #J" then
escape !temp_cierror
endif
if bound(job_nbr_streamed) = FALSE then
setvar job_nbr_streamed 0
endif
setvar job_nbr_streamed !job_nbr_streamed + 1
if !job_nbr_streamed > 100 then
setvar job_nbr_streamed 1
endif
setvar jobhist_!job_nbr_streamed "!stream_out !hptimef !stream_jobname"
echo Job !stream_out !hptimef !stream_jobname
|1jobhist|
showvar jobhist&@ > jobhistt
print jobhistt
.style body
.page 4
|3Listing Spoolfiles|
In order to see the output spoolfile from a job, you must first
know its spoolfile number. This task is really a number of steps which
are easily replaced by the following command file.
.style example
|1ljob|
parm jnum="&@",listcommand="/ljq"
if "!hpjobtype" <> "S" then
echo I'm sorry, but LJOB can only be run
from a session.
return
endif
setvar ljob_jnum ups("!jnum")-"J"-""
if not numeric("!ljob_jnum")
echo
echo usage: LJOB job_number
<,print command = '/LQJ'>
echo
echo eg. LJOB 1010,/lqj
(use Qedit lqj command)
echo LJOB 590,/lqj ]-50
(list last 50 lines)
echo LJOB 42
return
endif
setjcw cierror = 0
continue
spoolf o&@;seleq=[jobnum=#j!ljob_jnum and
filedes="$STDLIST"] &&
;show >ljobtmp
if cierror <> 0 then
setvar ljobtmp_eof 0
else
setvar ljobtmp_eof finfo("ljobtmp","eof")
endif
if ljobtmp_eof < 4 then
echo Spoolfile for job #&!ljob_jnum does not exist.
else
print ljobtmp;start=4;end=4 >ljobtmp2
input spooline < ljobtmp2
setvar spoolid str("!spooline",2,7)
!listcommand !spoolid.out.hpspool
endif
.style body
The default print command in the Ljob command file is |5/lqj|, which is
a Qedit command to list the file
without displaying line numbers, and to pause every 22 lines. If you do not have
Qedit, you can set the default list command to "print".
.page 4
|3CI Programming Inside Qedit|
You can use all MPE/iX CI commands in Qedit. One of the neatest tricks you
can try is to get a line of data from a Qedit file into a variable. You can do this with
one of Qedit's undocumented features: put a colon in front of the "/"
in a Qedit command, and
then on the next line, use an Input command.
For example,
.style example
comment This command file puts the data from the current
comment line into the variable, myline
/set list init off
/:/listq * > tempfile
/set list init on
input myline < tempfile
.style body
As printing is by nature a complex operation, there is no simple way for
Qedit to handle carriage control.
Depending on the source file, it is possible that Qedit would decide to
write an initial cctl record to the re-directed output file.
This is an
undesirable feature in most cases.
To ensure that this does not occur, use
the above
undocumented Qedit command, |2set list init off|.
After you are finished with your re-directions, you must remember to set list init on.
This trick can really help you when you're adding custom features to Qedit. Use the following
command file to find COBOL paragraph names without changing the current line pointer.
I use this feature when I can't recall the exact name of the paragraph that I want to reference.
.style example
|1findpara|
parm para_name left_window=7
:/ver zz > zztemp {save old zz in temp file}
/zz */* {save the current line number}
setvar g_para_name "!para_name"
setvar g_left_window "!left_window"
setvar right_window len("!g_para_name")
+ !g_left_window
/list "!para_name" (!g_left_window/!right_window)
:/list zz > $null {return to line number w/o seeing
the line number
/useq zztemp {restore old zz}
.style body
.break
You can invoke Findpara at any time, but I usually use it while in Visual mode.
Paragraph names must start in column eight for this command file to work properly.
You'll notice that Findpara saves your old ZZ marker in a file, and restores the original after doing its work.
Qedit
doesn't have this feature. Hey! We just extended Qedit's command set.
With a little bit of work, we could even have multiple bookmarks. I'll leave this as an exercise for the reader.
|3Where to go from here...|
No doubt you've often heard that the only way to learn
something is to actually do it. This thought applies directly to learning to
program the CI - you must jump in and get your feet wet. Start programming
the CI today!
A very well written HP manual, the |2MPE/iX Commands Reference Manual|,
is available on CD-ROM and on paper. I recommend
that you at least browse through it. Pay particular attention to Appendices A, B and
C in Volume 2 which deal mostly with handling
variables.
You can get instant help on the subject of variables. As of MPE/iX 5.0,
the on-line Help command |5help variables| provides pages
of text. (In pre-5.0 environments, the on-line Help in MPEX from VESOFT
will display approximately the same thing.)
And of course, if you really get stuck, you always have the Internet, and the list-server HP3000-L.
But that's a topic for another paper entirely.
.style example
while awake = TRUE
program the_ci
endwhile
.style body
.page
|3Glossary|
Some acronyms require no explanation; others are fairly obscure to the
uninitiated.
This short glossary
explains some of the terms and acronyms specific to this paper.
.mar (l+14)
.par (s0+ u14)
`3GL #+1Third Generation Programming Language. COBOL, FORTRAN and Pascal are 3GL's.
`4GL #+1A programming language for managers; requires less knowledge about the operating
system and/or hardware internals.
`CI #+1Command Interpreter. This is the program that is run right after you log on.
`MPE/V #+1Mostly harmless. Precursor to the MPE/iX operating system, run on overworked
machines.
`HP3000-L #+1A list-server available via the Internet. Lots of juicy tidbits can
be gleaned from this list.
`Hppath #+1A string variable used by the CI as a reference to determine where to look for
programs and command files to execute.
`Interpreted Code#+1Programs that are compiled "on the fly". Each statement in an interpreted
program is evaluated every time it is executed. Interpreted code tends to be a lot slower to
execute than compiled code.
`IPROF #+1|1I|nterex |1Pro|grammers |1F|orum, held every spring at the HP Cupertino labs.
`JCW #+1Job Control Word. An integer variable limited to the CM space.
`Local Variable #+1A variable that can be referenced only within some "local" area,
such as a command file.
`Macro #+1A collection of commands designed to perform a task more efficiently.
`Shell #+1A program, such as the CI, that is used as a working environment for launching
processes, editing code, and maintaining a command interface.
`UDC #+1User Defined Command. A file of callable commands with multiple
entry points. See page 2 of this paper.
`WYGIWYG#+1What You Got Is What You Get. Basically software without support.
.mar.par
.page
.style body
|3Addendum|
Although this table appears in the |2MPE/iX Commands Reference Manual|, you may not always have access
to it. I am inserting it here for convenience.
.opt (f- l- r-)
Expression Evaluator Functions
.opt
.opt (f-)
.if not outhelpcomp
.font 6
Symbol Function Example Result
.box (h0)
+(numeric) addition 4 + 5 9
+(string) concatenate "abc" + "de" abcde
-(numeric) subtraction 12 - 6 6
-(string) deletion of first "abc" - "b" ac
occurrence
* multiplication 4 * 5 20
/ integer division 79/ 10 7
^ exponentiation (9) 2^3 8
either " or ' string identifier either "abc" or 'abc' abc
() parentheses (3 + 4) * 2 14
< less than (1) 5 < 6 TRUE
<= less than or equal "abc" <= "abc" TRUE
(1)
> greater than (1) "xyz" > "abc" TRUE
>= greater than or "abc" >= "abc" TRUE
equal (1)
<> not equal 5 <> 6 TRUE
= equal "xyz"= "xyz" TRUE
ABS(integer) absolute value abs(-4) 4
ALPHA(string) check if a string is alpha('abcd') TRUE
alphabetic alpha('ab3d ef') FALSE
ALPHANUM(string) check if a string is alphanum('abCd') TRUE
only alphabetics and alphanum('45abd') TRUE
digits alphanum('3d ef') FALSE
AND logical and 7=7 and 5=5 TRUE
BAND bitwise and 7 band 13 5
BNOT bitwise not bnot 5 -6
BOR bitwise or 5 bor 2 7
BOUND(varname) variable bound(HPPATH) TRUE
definition
test (2)
BXOR bitwise 7 bxor 5 2
exclusive or
CHR(integer) ASCII value chr(65) A
(integer) ===>
character
CSL circular shift -2 csl 2 -5
left (3)
CSR circular shift -7 csr 1 -4
right (3)
DWNS(string) shift string dwns('aBC&dE') abcde
to lowercase
(7)
FINFO(filename,option) file FINFO('x.pub',0) TRUE
information
(6)
HEX(integer) convert to hex(329) $149
hexadecimal
string
INPUT([prompt][,wait]) accept user input('Enter Enter choice: Y
input (10) choice:',20) Return "Y"
LEN(string) string length len("abc") 3
LFT(string, chars) left string lft('abc',2) ab
extraction
LSL logical shift 7 lsl 1 14
left
LSR logical shift -7 lsr 1 2,147,483,644
right
LTRIM(string[,trimstr]) trim left end 'X'+ltrim(' abc') Xabc
of string (11) "X"+ltrim('...abc', Xabc
'.')
MAX(num1[,num2...]) find largest max(5,4-3,70,0) 70
of several
integers
MIN(num1[,num2...]) find smallest min(5,4,-3,70,0) -3
of several
integers
MOD modulo (4) 25 mod 2 1
NOT logical not not(2>1) FALSE
NUMERIC (string) check if a numeric('12345') TRUE
string is all numeric('$a234ef') FALSE
digits
OCTAL(integer) convert to octal(329) %511
octal string
ODD(integer) determine if odd(233) TRUE
integer is odd odd(-2) FALSE
OR logical or 5=5 or 2=3 TRUE
ORD(string) ordinal (8) ord('AbcD') 65
POS(find str,source find Nth pos('ab','cgabd') 3
str[,n]) occurrence of pos('.','file.grp.acct ',2) 9
find str in pos('.','file.grp.acct ',- 9
source str (-N 1)
searches from
right) (12)
RHT(string, chars) right string rht("abc",2) bc
extraction
RPT(string,count) repeat a rpt('aBc',3) aBcaBcaBc
string (-count rpt('aBc',-3) cBacBacBa
reverses
string)
RTRIM(string[,trimstr]) trim right end rtrim('abc ')+'X' abcX
of string (11) rtrim('abc...','.')+"X " abc X
SETVAR(varname,expr) return result setvar(myvar,2*3+5) sets variable
@@@@@@@@@@@@@@@@@@@@ of expr and @@@@@@@@@@@@@@@@@@@ myvar to 11 and
@@@@@@@@@@@@@@@@@@@@ set varname to @@@@@@@@@@@@@@@@@@@ returns 11
@@@@@@@@@@@@@@@@@@@@ result (13) @@@@@@@@@@@@@@@@@@@
STR(string,start pos, general string str('abcde',2,3) bcd
chars) extraction
TYPEOF(expression) type of typeof(HPPATH) 2 (string)
variable or
expression (5)
UPS(string) shift string ups('aBc5d') ABC5D
to uppercase
(7)
XOR logical 7=7 xor 5=5 TRUE
exclusive or
.else
Symbol Function Example Result
+(numeric) addition 4 + 5 9
+(string) concatenate "abc" + "de" abcde
-(numeric) subtraction 12 - 6 6
-(string) deletion of first "abc" - "b" ac
occurrence
* multiplication 4 * 5 20
/ integer division 79/ 10 7
^ exponentiation (9) 2^3 8
either " or ' string identifier either "abc" or 'abc' abc
() parentheses (3 + 4) * 2 14
< less than (1) 5 < 6 TRUE
<= less than or equal "abc" <= "abc" TRUE
(1)
> greater than (1) "xyz" > "abc" TRUE
>= greater than or "abc" >= "abc" TRUE
equal (1)
<> not equal 5 <> 6 TRUE
= equal "xyz"= "xyz" TRUE
ABS(integer) absolute value abs(-4) 4
ALPHA(string) check if a string is alpha('abcd') TRUE
alphabetic alpha('ab3d ef') FALSE
ALPHANUM(string) check if a string is alphanum('abCd') TRUE
only alphabetics and alphanum('45abd') TRUE
digits alphanum('3d ef') FALSE
AND logical and 7=7 and 5=5 TRUE
BAND bitwise and 7 band 13 5
BNOT bitwise not bnot 5 -6
BOR bitwise or 5 bor 2 7
BOUND(varname) variable bound(HPPATH) TRUE
definition
test (2)
BXOR bitwise 7 bxor 5 2
exclusive or
CHR(integer) ASCII value chr(65) A
(integer) ===>
character
CSL circular shift -2 csl 2 -5
left (3)
CSR circular shift -7 csr 1 -4
right (3)
DWNS(string) shift string dwns('aBC&dE') abcde
to lowercase
FINFO(filename file FINFO('x.pub',0) TRUE
,option) information
HEX(integer) convert to hex(329) $149
hexadecimal
string
INPUT([prompt] accept user input('Enter Enter choice: Y
[,wait]) input (10) choice:',20) Return "Y"
LEN(string) string length len("abc") 3
LFT(string, left string lft('abc',2) ab
chars) extraction
LSL logical shift 7 lsl 1 14
left
LSR logical shift -7 lsr 1 2,147,483,644
right
LTRIM(string trim left end 'X'+ltrim(' abc') Xabc
[,trimstr]) of string (11) "X"+ltrim('...abc', Xabc
'.')
MAX(num1[,num2...]) ind largest max(5,4-3,70,0) 70
of several
integers
MIN(num1[,num2...]) find smallest min(5,4,-3,70,0) -3
of several
integers
MOD modulo (4) 25 mod 2 1
NOT logical not not(2>1) FALSE
NUMERIC (string) check if a numeric('12345') TRUE
string is all numeric('$a234ef') FALSE
digits
OCTAL(integer) convert to octal(329) %511
octal string
ODD(integer) determine if odd(233) TRUE
integer is odd odd(-2) FALSE
OR logical or 5=5 or 2=3 TRUE
ORD(string) ordinal (8) ord('AbcD') 65
POS(find str,source find Nth pos('ab','cgabd') 3
str[,n]) occurrence of pos('.','file.grp.acct',2) 9
find str in pos('.','file.grp.acct',- 9
source str (-N 1)
searches from
right) (12)
RHT(string, chars) right string rht("abc",2) bc
extraction
RPT(string,count) repeat a rpt('aBc',3) aBcaBcaBc
string (-count rpt('aBc',-3) cBacBacBa
reverses
string)
RTRIM(string trim right end rtrim('abc ')+'X' abcX
[,trimstr]) of string (11) rtrim('abc...','.')+"X " abc X
SETVAR(varname,expr) return result setvar(myvar,2*3+5) sets variable
of expr and myvar to 11 and
set varname to returns 11
result (13)
STR(string,start general string str('abcde',2,3) bcd
pos, chars) extraction
TYPEOF(expression) type of typeof(HPPATH) 2 (string)
variable or
expression (5)
UPS(string) shift string ups('aBc5d') ABC5D
to uppercase
(7)
XOR logical 7=7 xor 5=5 TRUE
exclusive or
.endif
.mar