home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.robelle3000.ai 2014
/
2014.06.ftp.robelle3000.ai.tar
/
ftp.robelle3000.ai
/
papers
/
progci.txt
< prev
next >
Wrap
Text File
|
1995-07-09
|
43KB
|
1,011 lines
PAPER NUMBER 4018
Programming the Command Interpreter
An Introduction, a Dog, and New Tricks
by Ken Robertson
May 1995
Robelle Consulting
15399-102A Avenue
Surrey, BC Canada V3R 7K1
(604) 582-1700
Introduction
An overworked, understaffed data processing department is all too common in
today's ever belt-tightening, down-sizing 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.
A 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.
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 (Command Interpreter) was
ported to MPE/iX machines, and beefed up so it would be usable as a run-time
shell.
The MPE/iX command interpreter has a generous command set, pushing 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. Its code, however, is interpreted, which
may cause a CI solution to a problem to execute too slowly for practical
purposes. In this case a 3GL, 4GL, or third party software solution would
would have to be investigated.
What 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 in the CI interactively, you can do with command files, and
then some. You can use command files in situations that call for repetitive
functions, such as re-compiling 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 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 convention commonly used is to create 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.
***********************
logonudc
option logon, nobreak
logon.cmd.prod
bye
The above UDC is invoked at logon, and executes the command file called
logon.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 OPTION NOHELP to prevent snooping eyes from reading the contents of a
particular user command. [Note: A future operating system release will allow
XEQ access for command files. Currently, you must have XEQ and READ access to
the files in order to use them.]
The Command File Structure
The structure of a command file is as follows:
PARM parameter declarations (optional)
MPE command
.
.
.
MPE command
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.
(Note to Qedit users: Qedit has a maximum recursive level of 10, which should
be enough.)
The 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, keep and test your
command files, saving 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 sj.
echo echo All executing jobs: > sj
echo showjob;exec >> sj
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:
anyparm search_string = "?"
if "!search_string" = "?" then
echo command file: phone search_string
return
endif
run grep.bin.hpsys "-c !search_string"
How can you echo variables into a file without getting the assigned value of
the variable? Say, for example the value of the variable my_time was equal to
"10:02". The statement
echo echo The time is !mytime > cmdfile
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:
echo echo The time is !!mytime > cmdfile
That would write the string "echo The time is !my_time" into the first record
of Cmdfile.
Variables
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 256 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, and the name must start with either a letter or an
underbar.
Setting and Displaying Variables
You have two methods for setting variables: use either the typical assignment
command, or prompt a value from a terminal.
1. setvar variable_name expression
2. input variable_name prompt
The expressions for Setvar can be made up of either numerics, strings,
booleans, variable names, or variable de-referencing (sort of like pointers),
all glued together with operators. For example, to concatenate the values of
two string variables, you can do the following:
setvar group_account "!hpgroup" + "." + "!hpaccount"
If the logon group and account were PUB and SYS, the above would set the
variable group_account to the value of PUB.SYS. Figure 1 shows a list of all
of the operators possible in a Setvar command.
Logical operators: AND, OR, XOR, NOT
Boolean functions and values: BOUND, TRUE, FALSE, ALPHA, ALPHANUM, NUMERIC, ODD
Comparison operators: =, <>, <, >, <=, >=
Bit manipulation operators: LSL, LSR, CSR, CSL, BAND, BOR, BXOR, BNOT
Arithmetic operators: MOD, ABS, * , / , + , -, ^ (exponentiation)
Functions returning strings: CHR, DWNS, UPS, HEX, OCTAL, INPUT, LFT, RHT, RPT,
LTRIM, RTRIM, STR
Functions returning integers: ABS, LEN, MAX, MIN, ORD, POS, TYPEOF
Other functions: FINFO, SETVAR
figure 1. Setvar Operators
Variable 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 uppercase. To see a list
of them, simply enter the command
showvar hp@
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,
parm local_myvar="Not Available In Stores"
echo !local_myvar
If after executing the above command file, you do a showvar local_myvar, the
variable will not exist, and a CI error will grace your screen. The temporary
variable my_var existed only during the execution of the command file.
Local variables cannot be modified, and are accessed only 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.
hithere
parm hpjobname="ZZTOP"
echo Good evening, !hpjobname.
showvar hpjobjname
Exexcuting Hithere gives us
Good evening, ZZTOP
HPJOBNAME = KEN
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 only within the command file that defines
it.
Getting 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 re-direction and the print
command. I/O re-direction allows input or output to be directed to a file
instead of to your terminal. I/O re-direction uses the symbols ">", ">>" and
"<". Use ">" to re-direct 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.
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.
After executing the above command file, the file Myfile will contain two
lines, "Value 42" and "This is the second line". (Without quotes, of course.)
The Input command uses I/O re-direction to read the first record of the file,
and assigns the value to the variable my_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 consequent lines of a file to
variables? You use the Print command to select the record that you want from
the file, sending the output to a new file.
print myfile;start=2;end=2 > myfile2
You can then use the Input command to extract the string from the second file.
Rolling 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:
setvar my_printer "biglaser"
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.
byebye
purge mygvars > $null
file mygvars;save
echo setvar my_printer "!my_printer" > *mygvars
bye
Whenever you type byebye, the setvar command is written to Mygvars and you are
then logged off. The default close disposition of an I/O re-direction 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.
Program Control - If/Else and While
One of the reasons I like programming the CI is its lack of goto's. This
much-debated programming style has sparked many a heated discussion. Consider
the following:
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
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 exit 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.
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.
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.
escape 007
Simulating Arrays
It's true - arrays are not directly supported in the CI. However, because of
some undocumented techniques (read: tricks), 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.
I won't keep you in suspense. Here's the core method:
setvar !variable_name!variable_index value
By using the expression evaluation feature of the CI, you can have a
changeable variable name in the Setvar command. CAVEAT 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
after 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, i.e., to create self-modifying code. For example,
weirdcmd
setvar variable_command "setvar apple ""orange"""
!variable_command
If you run the command file, and then display the contents of the variable,
you will see
weirdcmd {execute command file, above}
showvar apple
APPLE = orange
To simulate arrays, you must assign a variable per 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.
arraydef
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
The command file Arraydef allocates variables for each element of the array
that you need. The call sequence would be something like
arraydef months_ 12
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 bytes. The space used can be calculated by adding
the number of characters in each variable name, plus 4 bytes per integer item,
or the length of each string. 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:
Symbol table full: addition failed. To continue, delete
some variables, or start a new session. (CIERR 8122)
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 using it.
arraydel
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
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.
arrayset
parm array_name element_nbr=0
anyparm set_value
setvar !array_name!element_nbr !setvalue
arrayget
parm destination_var array_name element_nbr=0
anyparm get_value
setvar !destination_var !array_name!element_nbr
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.
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
Calling 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.
input yes_no,"Please enter yes or no: "
if ups("!yes_no") = "YES" then
Since people generally like to reply with one-character answers, you can add
one more function to your command file.
input yes_no,"Please enter yes or no: "
if lft(ups("!yes_no"),1) = "Y" then
In the above two examples, the ups() function upshifts the argument string.
lft() returns the number of characters specified starting 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 not say
if lft("ups("!yes_no")",1) = "Y" then
Because of CI expansion rules, you cannot use quotes when you embed a function
within a function. If you think about it, the ups() function in the above
statement would never get resolved. If you play computer and expand the above
statement, you get the following:
IF lft("ups("yes")",1) = "Y" THEN
which, when executed, produces the error, A string operator was expected but
none was found. (CIERR 9755)
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
Two of the HP manual MPE/iX Commands.
dwns( ) Shift string to lowercase
finfo( ) Returns file info, such as eof, etc.
rht( ) Returns rightmost count of characters
str( ) Extract a portion from the middle of a string
Popular 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.
Obtaining 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 not work as you expect:
remote purge bigfile
if cierror <> 0 then
echo error!
endif
In fact, the HP Commands reference manual states:
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).
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.
remci.cmd.prod
remote echoci tempci
remote purge tempci > $null
remote save tempci
dscopy tempci:remmach;tempci;rep
continue
tempci
echoci.cmd.prod
parm tempfile
echo setjcw cierror !cierror > !tempfile
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 that the Remci command
file does is to remotely invoke the Echoci command file. This inserts the
line "setjcw 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 cierror to the value of the remote cierror.
You can now insert this command file call into your first jobstream and make
it work.
remote purge bigfile
remci
if cierror <> 0 then
echo error purging remote bigfile!
endif
Job 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.
checkjob
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
A gotcha about grep: grep will only read permanent 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 -c option used above
tells grep to return only the count of strings found, while the i indicates
that the search shouldn't care about upper or lower-case. (Ignore case, I
guess.) In our command file, the search file is in the MPE name space, and
must appear in UPPER CASE.
Displaying 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.
sja
echo JOBNUM STATE IPRI JIN JLIST INTRODUCED
JOB NAME&[esc]&a65C[esc]&d!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
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 [esc] indicates the escape character, ascii 27. That is, [esc]&dB
is the escape sequence to turn on inverse video.
Creating a Job History
Have you ever streamed a job, cleared your screen, then wished you had
remembered 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 jobhist_#, 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.
jstream
anyparm stream_jobname = ?
echo JSTREAM 1.2 Type JOBHIST to see your jobstream history.
if finfo("!stream_jobname",0) = 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
Jobhist
showvar jobhist > jobhistt
print jobhistt
Listing 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.
ljob
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
endif
setvar ljobtmp_eof finfo("ljobtmp","eof")
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
The default print command in the Ljob command file is /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".
CI 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,
comment This command file puts the data from the current line
comment into the variable, myline
/listq *
/:/listq * > tempfile
input myline < tempfile
Qedit versions 4.3 to 4.16 are inconsistent when you do a listq * > Tempfile
first without doing a listq *. Tempfile may or may not contain two lines,
with a blank first line. Doing a listq * guarantees that tempfile will
contain only the current line of data, and no preceeding blank line.
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.
findpara
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}
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.
Where 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 MPE/iX Command 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 Two,
which deal mostly with handling variables.
You can get instant help on the subject of variables. As of MPE/iX 5.0 the
online Help command, help variables, will output pages of text. (In pre-5.0
environments, the online 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.
while awake = TRUE
program the_ci
endwhile
Glossary
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.
3GL Third Generation Programming Language. COBOL, FORTRAN and
Pascal are 3GL's.
4GL A programming language for managers - requires less knowledge
about the operating system and/or hardware internals.
CI Command Interpreter. This is the program that is run right
after you log on.
MPE/V Mostly harmless. Pre-cursor to the MPE/iX operating system, run
on overworked machines.
HP3000-L A list-server available via the Internet. Lots of juicy tidbits
can be gleaned from this list.
HPPATH A string variable used by the CI as a reference to determine
where to look for programs and command files to execute.
Interpreted CodePrograms 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 Interex Programmers Forum, held every spring at the HP Cupertino
labs.
JCW Job Control Word. A integer variable limited to the CM space.
Local Variable A variable that can be referenced only within some "local"
area, such as a command file.
Macro A collection of commands designed to perform a task more
efficiently.
Shell A program, such as the CI, that is used as a working environment
for launching processes, editing code, and maintaining a command
interface.
UDC User Defined Command. See page 2 for a description.
WYGIWYG What You Got Is What You Get. Basically software without
support.
Addendum
Although this table appears in the Commands Reference manual, its not always
convenient to browse through such a tome. I am inserting it here for
convenience.
Expression Evaluator Functions
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') abc#de
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