|
1
|
- Jeff Vance, HP-vCSY
jeff.vance@hp.com
- Hewlett-Packard
|
|
2
|
- UDCs and scripts (parameters, entry points)
- Variables
- Expressions and functions
- I/O redirection and file I/O
- Error handling
- Script cleanup techniques
- Debugging
- Converting a quick’n’dirty script to near production quality
- Examples
- Appendix
|
|
3
|
- 3GL (C, COBOL, Java, Pascal, compiled Basic, etc.)
- 4GL (Speedware, Transact, Powerhouse, Visual Basic, etc.)
- Interpretive (CI, Basic, other scripting languages)
|
|
4
|
- IF, ELSEIF, ELSE,
ENDIF branching
ESCAPE, RETURN
- WHILE, ENDWHILE looping
- ECHO, INPUT terminal, console
I/O, file I/O
- SETVAR, DELETEVAR
create/modify/delete/display a variable
SHOWVAR
- ERRCLEAR sets CI error variables to 0
- RUN invoke a program
XEQ invoke a program or script
- PAUSE sleep; job synchronization
- OPTION recursion only way to get recursion in UDCs
- # or COMMENT comment
|
|
5
|
- User Defined Command files (UDCs) - a single file that contains 1
or more command definitions,
separated by a row of asterisks (***)
- Features:
- simple way to execute several commands via one command
- allow built-in MPE commands to be overridden
- can be invoked each time the user logs on
- require lock and (read or eXecute) access to the file
- cataloged (defined to the system) for easy viewing and prevention of
accidental deletion -- see SETCATALOG and SHOWCATALOG commands
- can be defined for each user or account or at the system level
- more difficult to modify since file is usually opened by users
|
|
6
|
- Command file - a file that contains a single command definition
- Features:
- similar usage as UDCs
- searched for after UDCs and built-in commands using HPPATH
- default path is: logon-group, PUB.logon-acct, PUB.SYS, ARPA.SYS
- require read or eXecute access
- easy to modify since file is only in use while it is being executed
- very similar to unix scripts or DOS bat files
|
|
7
|
- Similarities:
- ASCII, NOCCTL, numbered or unnumbered, max 511 byte record width
- optional parameter line ok - max of 255 arguments
- optional options, e.g. HELP, NOBREAK, RECURSION
- optional body (actual commands)
- no inline data, unlike Unix
‘here’ files :(
- can protect file contents by allowing eXecute access-only security,
i.e., denying read access
|
|
8
|
- Differences:
- scripts can be variable record width files
- UDCs require lock access, scripts don’t
- script names can be in POSIX syntax, UDC filenames must be in MPE
syntax
- UDC name cannot exceed 16 chars, script name length follows rules for
MPE and POSIX named files
- EOF for a script is the real eof, end of a UDC command is one or more
asterisks, starting in column one
|
|
9
|
- EOF -- real EOF for scripts, a row of asterisks (starting in column 1)
for UDCs
- :BYE, :EOJ, :EXIT -- terminate the CI too, to use BYE or EOJ must be the
root CI
- :RETURN -- useful for entry point exit, error handling, help text -
jumps back one call level
- :ESCAPE -- useful to jump all the
back to the CI, or an active :CONTINUE.
In a job without a :CONTINUE, :escape terminates the job. Sessions are not terminated by
:escape. Can optionally set
CIERROR and HPCIERR variables to an error number
|
|
10
|
- UDCs provide a repository and are easier to locate, but they are more
difficult to change after they have been cataloged. They are also more
difficult to purge (deliberately or accidentally).
- Scripts can be located anywhere but it is easier to maintain a
collection of scripts if they are kept in one or a few groups /
directories. Scripts are easier to modify and delete.
- My experience has been to use scripts as my first choice and only use
UDCs if I need to override a built-in MPE command.
- Use ESCAPE rather than RETURN for script errors that demand user
attention.
|
|
11
|
- Syntax: ParmName [ = value ]
- supplying a value means the parameter is optional. If no value is defined the parameter
is considered required.
- max parm name is 255 bytes, chars A-Z, 0-9, “_”
- max parm value is limited by the CI’s command buffer size (currently
511 characters)
- all parm values are un-typed, regardless of quoting
- Parms are separated by a space, comma or semicolon
- default value may be a: number, string, !variable, ![expression], an
earlier defined parm (!parm)
- all parameters must be explicitly referenced in the UDC/script body,
e.g. !parmname
- the scope of a parm is the body of the UDC/script
|
|
12
|
- all parameters are passed “by value”, meaning the parm value cannot be
changed within the UDC/script
- a parm value can be the name of a CI variable, thus it is possible for a
UDC/script to accept a variable name, via a parm, and modify that
variable’s value, e.g.
- SUM a, b, result_var SUM is a UDC name
setvar !result_var !a + !b
*****
- :SUM 10, 2^10, x
:showvar x X = 1034
- :setvar I 10
:setvar J 12
:SUM i, j, x inside SUM:
setvar x, i + j
:showvar x X = 22
|
|
13
|
- all delimiters ignored
- must be last parameter defined in UDC/script
- only one ANYPARM allowed
- only way to capture user entered delimiters, without requiring user to
quote everything
- example:
- TELLT user
ANYPARM msg = “”
# prepends timestamp
and highlights msg text
tell !user; at
!hptimef: ![chr(27)]&dB !msg
-
:TELLT op.sys Hi,, what’s up; system seems
fast!
FROM S68 JEFF.UI/3:27
PM: HI,, what’s up; system seems…
- anyparm( ) function is useful with ANYPARM parameters
|
|
14
|
- simple convention for executing the same UDC/script starting in
different “sections” or subroutines
- a UDC/script invokes itself recursively passing in the name of an entry
(subroutine) to execute
- the script detects that it should execute an alternate entry and skips
all the code not relevant to that entry.
- most useful when combined with I/O redirection, but can provide the
appearance of generic subroutines
- benefits are: fewer script files to maintain, slight performance gain
since MPE opens an already opened file faster, can use variables already
defined in script
- UDCs need OPTION RECURSION to use multiple entry points
|
|
15
|
- two approaches for alternate entries:
- define a parm to be the entry point name, defaulting to the main part
of the code, for example: “main”
- the UDC/script invokes itself recursively in the main code, and may use
I/O redirection here too
- each entry point returns when done (via :RETURN command)
---------------------------
or ---------------------------------
- test HPSTDIN or HPINTERACTIVE variable to detect if script/UDC has I/O
redirected.
- if TRUE then assume UDC/script invoked itself.
- limited only to entry points used when $STDLIST or $STDIN are
redirected
- limited to a single alternate entry point, may not work well in jobs
|
|
16
|
- generic approach:
- PARM p1 … entry=main # default entry is
“main”
- if “!entry” = “main” then
- … initialize etc…
- xeq !HPFILE !p1, … entry=go #
run same script, different entry
- … cleanup etc…
- return
- elseif “!entry” = “go” then...
- # execute the GO subroutine
...
- return
- elseif “!entry” = …
...
- endif
|
|
17
|
- I/O redirection specific approach:
- PARM p1 … # no “entry” parm
defined
- if HPSTDIN = “$STDIN” then
- # assume “main” entry --
initialize etc…
- xeq !HPFILE !p1, … <somefile
- … (cleanup etc…)
- return
- else # no elseif since only 1 alternate
- # execute the entry to read
“somefile”
- setvar eof FINFO(hpstdin,
“eof”)
- …
- return
- endif
|
|
18
|
- Comment all parameters and their expected and default values. Equally
important for entry points since args may be used differently and input
and/or output may have been redirected.
- Define good default parm values and allow some obvious value for the
first parm (“?”) to signify script-specific help. Sometimes an absent
first parm should imply help text needs to be displayed.
- Choose parameter names which do not collide with the variable names in
the script/UDC.
- Use “entry points” to make scripts more structured and for file I/O. The
parameter based alternate entry approach is superior from a flexibility
perspective since it works in all environments and is easily expanded.
|
|
19
|
- 100 predefined “HP” variables* in MPE/iX release 7.0
- user can create and modify their own variables via :SETVAR
- variable types are: integer (signed 32 bits), Boolean and string (up
1024 characters)
- variable names can be up 255 alphanumeric alphanumeric and “_” (cannot
start with number)
- predefined variable cannot be deleted, some allow write access
- :SHOWVAR @ ; HP -- shows all
predefined variables
- can see user defined variables for another job/session (need SM)
- :SHOWVAR @ ; job=#S or #Jnnn
- the bound( ) function returns true if the named variable exists
- variables deleted when job / session terminates
- :HELP variables and :HELP VariableName
|
|
20
|
- HPAUTOCONT - set TRUE causes CI
to behave as if each command is protected by a :continue.
- HPCMDTRACE - set TRUE causes UDC / scripts to echo each command line as
long as OPTION NOHELP not specified.
Useful for debugging.
- HPCPUMSECS - tracks the number of milliseconds of CPU time used by the
process. useful for measuring
script performance.
- HPCWD - current working directory in POSIX syntax.
- HPDATETIME - contains the date/time in
CenturyYearMonthDateHourMinuteSecondMicrosecond format.
- HPDOY - the day number of the year from 1..365.
- HPFILE - the name of the executing script or UDC file.
- HPINTERACTIVE - TRUE means $STDIN and $STDLIST do not form an
interactive pair, useful to test if it is ok to prompt the user.
- HPLASTJOB - the job ID of the job you most recently streamed, useful for
a default parm value in UDCs that alter priority, show processes, etc.
|
|
21
|
- HPLASTSPID - the $STDLIST spoolfile ID of the last job streamed, useful
in
:print !hplastspid.out.hpspool
- HPLOCIPADDR - IP address for your system.
- HPMAXPIN - the maximum number of processes supported on your system.
- HPPATH - list of group[.acct] or directory names used to search for
script and program files
- HPPIN - the Process Identification Number (PIN) for the current process.
- HPPROMPT - the CI’s command prompt, useful to contain other info like:
!!HPCWD, !!HPCMDNUM, !!HPGROUP, etc.
- HPSPOOLID - the $STDLIST spoolfile ID -- if executing in a job.
- HPSTDIN - the filename for $STDIN, useful in script ”subroutines” where
input has been redirected to a disk file
- HPSTREAMEDBY - the “Jobname,User.Acct (jobIDnum)” of the job/session
that streamed the current job.
- HPUSERCAPF - formatted user capabilities, useful to test if user has
desired capability, e.g. if pos(“SM”,hpusercapf) > 0 then
|
|
22
|
- Define your own variables to not appears as HP variables and chose
unique names, e.g. I, J, K, NAME, TEMP are not meaningful names for any
variable which survives the scope of its creation. NUM_CUSTOMERS,
PAYROLL_FILENAME, etc. are more descriptive names.
- Don’t define parameters with the same names as your variables and
vice-versa -- just not worth the extra confusion.
- In general don’t use HPAUTOCONT since it can mask errors in your
script/UDC.
- Be careful using the date/time variables. Remember your script could be
running when the clock just passes midnight, or the month or year just
advances.
- Use formatted vs. numeric variables. E.g. HPUSERCAPF is preferred to
HPUSERCAP.
- Use HPFILE to avoid hard-coding the name of your script.
- Use HPINTERACTIVE to avoid prompting in a job.
|
|
23
|
- functions are invoked by their name, accept zero or more parms and
return a value in place of their name and arguments
- file oriented functions:
- BASENAME, DIRNAME, FINFO,
FSYNTAX, FQUALIFY
- string parsing functions:
- ALPHA, ALPHANUM, DELIMPOS, DWNS, EDIT, LEN, LFT, LTRIM, NUMERIC,
PMATCH, POS, REPL, RHT, RPT, RTRIM, STR, UPS, WORD, WORDCNT, XWORD
- conversion functions:
- CHR, DECIMAL, HEX, OCTAL, ORD
- arithmetic functions
|
|
24
|
- job/process functions:
- misc. functions:
- ANYPARM, BOUND, INPUT, SETVAR, TYPEOF
- new to 7.5: user defined functions:
- Function name is a filename. HPPATH is used to locate the function
file. RETURN command accepts an expression used as the function return.
HPRESULT variable holds the function return.
- Examples:
- if myFunc( a, b, c ) then ...
- if compare( result ) < compare( last ) then ...
- if get_user_data( start, end ) = 0 then ...
- if get_device_info( ldev, “state” ) = “READY” then ...
|
|
25
|
- an expression is any variable, constant or function with or without an
operator, e.g.:
- MYVAR, “a”+”b”, x^10*y/(j mod 6),
- false, (x > lim) or (input()
=“y”)
- 5 commands accept implicit expressions:
:calc, :if, :elseif, :setvar, :while
- ![ expression ] can be used explicitly
in any command:
:build afile; rec=-80; disc= ![100+varX]
:build bfile; disc= ![ finfo(“afile”,”eof”)*3] # file b is 3x larger
- examples:
- :print ![input(“File name? “)]
- :setvar reply ups(rtrim(ltrim(reply)))
|
|
26
|
- The CI evaluates the minimal amount of a Boolean expression needed to
determine the end result. For
example:
- if true or x # “x” side not
evaluated
- if false and x # “x” side not
evaluated
- if bound(z) and z > 1 then # if “z” not defined it won’t be
referenced
- Partial evaluation can cause some mysterious results
- CI scripts may run differently in an MPEX environment since (last I
heard) MPEX does not support partial evaluation. In this case break up
complex expressions.
- :calc FALSE and pinfo( 0, ”xyz” ) = 0 returns FALSE, no error
- :calc FALSE and pinfo( 0, “exists” ) = 0 gets an error *
- :calc FALSE and setvar( x, true ) X not set
- :calc FALSE and setvar( x, 1 ) get an error *
- * A non-boolean expression was found. (CIERR 9940)
|
|
27
|
- why not use INPUT in WHILE to read a flat file?, e.g.:
- while not eof do
input varname < filename
endwhile
- three main alternatives:
- write to (create) and read from a MSG file via I/O redirection
- use :PRINT and I/O redirection to read file 1 record at a time
- use entry points and I/O redirection
- MSG files work because each read is destructive, so when INPUT <file
reads the 1st record it automatically gets the next record.
- PRINT works because start and end record numbers can be selected.
- once in an entry point where I/O has been redirected, you can easily
read a file.
|
|
28
|
- PARM fileset=./@
# This script reads LISTFILE,6 output and measures CPU
millisecs
# using a MSG file
setvar savecpu hpcpumsecs :readmsg
errclear 259 msecs to read 22 records
file msg=/tmp/LISTFILE.msg; MSG
continue :readmsg @.pub.sys
listfile !fileset,6 >*msg 15845 msecs to read 1515
if hpcierr = 0 then
# read listfile names
into a variable
setvar cntr setvar(eof,
finfo('*msg', "eof"))
while setvar(cntr,
cntr-1) >= 0 do
input rec <*msg
endwhile
endif
echo ![hpcpumsecs - savecpu] msecs to read !eof records.
deletevar cntr, eof, rec
|
|
29
|
- PARM fileset=./@
# This script reads a file produced by LISTFILE,6 and measures
CPU msecs
# using PRINT as an intermediate step
setvar savecpu hpcpumsecs
errclear :readprnt
continue 735 msecs to
read 22 records
listfile !fileset,6 > lftemp 3 times slower than MSG files
if hpcierr = 0 then
# read listfile names
into a variable :readprnt
@.pub.sys
setvar cntr 0 74478 msecs to read 1515 recs
setvar eof
finfo('lftemp',"eof")
over 4 times slower than MSG files!
while setvar(cntr,
cntr+1) <= eof do
print lftemp; start=!cntr;end=!cntr
> lftemp1
input rec <lftemp1
endwhile
endif
echo ![hpcpumsecs - savecpu] msecs to read !eof records.
deletevar cntr,eof,rec
|
|
30
|
- PARM fileset=./@, entry="main”
# This script reads a file produced by LISTFILE,6 and measures
CPU msecs
# using entry points and script redirection
if "!entry" = "main" then
setvar savecpu
hpcpumsecs
errclear
continue
listfile !fileset,6 >
lftemp
if hpcierr = 0
then
xeq !hpfile !fileset entry=read
<lftemp
endif
echo ![hpcpumsecs -
savecpu] msecs to read !eof records.
deletevar
cntr,eof,rec
purge lftemp;temp
return
. . . (continued on next
slide)
|
|
31
|
- else
# read listfile names
into a variable
setvar cntr setvar(eof,
finfo(hpstdin, "eof"))
while
setvar(cntr,cntr-1) >= 0 and setvar(rec, input()) <> chr(1)
do
endwhile
return
endif
- :readntry
90 msecs to read 22 records.
---> Almost 3 times faster than MSG files
---> 8 times faster than the PRINT method!
- :readntry @.pub.sys
2400 msecs to read 1515 records.
---> Over 6 times faster than MSG files
---> 31 times faster than using PRINT!
|
|
32
|
- Use variable names naturally (implicitly) – no explicit referencing
unless necessary.
- Use the more powerful string parsing functions (word, xword, wordcnt, delimpos,
edit) where possible.
- Enter :help functions and see if there are any surprises.
- Recognize partial evaluation, test the “skipped” clauses.
- Use “entry points” to make scripts more structured and for file I/O.
- Use MSG files for simple or one-time tasks, or for reading small files.
- Always, always write comments and log changes.
- Assume your quick’n’dirty script will stay in production longer than
you!
|
|
33
|
- use HPAUTOCONT variable judiciously. This is better:
continue
command
if hpcierr > 0 then
echo something…
return -- or -- escape
endif …
- RETURN vs. ESCAPE
- :return goes back ONE level
- :escape goes back to the CI level in a session, to an active CONTINUE,
or can abort a job
- HPCIERRMSG - variable contains the error text for the value of CIERROR
JCW / variable
- :ERRCLEAR - sets HPCIERR, CIERROR, HPFSERR, HPCIERRCOL variables to zero
|
|
34
|
- delete variables “local” to the UDC / script
- purge scratch files
- reset “local” file equations
- don’t do the above if still debugging!
- better to build in a way to preserve files, variables, etc. on the fly
- use a central cleanup “entry” routine
- use a variable to control the cleanup related commands
|
|
35
|
- Some common problems:
- syntax error (unmatched parenthesis), variable name typo, reliance on a
var that has not been initialized, hitting eof, using an HFS file for
I/O redirection and then referencing FINFO(hpstdin) -- CI bug!, entry
name typo (case sensitive!), off-by-one on loop counters, unexpected
user input, re-using the same
var in two places that are executed together (popular in entry points), reading from terminal but $stdin is
already redirected, a skipped portion of an expression or skipped
commands now being executed with different data...
- Trickier problems to find:
- echoing a literal “>” without escaping,word() by index but index out
of bounds, “array” index
increment and reference in same loop, unmatched endwhile or endif, creating files that could contain CI
metachars, date calculations that cross day, month, year boundaries...
|
|
36
|
- Real example taken from a request on 3000-L to report all program files
with PM capability.
- Need to consider NM and CM program files.
- Wanted a free solution.
|
|
37
|
- purge progf
purge versf
build progf;msg;rec=-80,,f,ascii
build versf;msg;rec=-80,,f,ascii
file x=progf,old
file y=versf,old
listfile @.@.@,6; seleq=[code=PROG] >*x
listfile @.@.@,6; seleq=[code=NMPRG] >>*x
setvar peof finfo('*x','eof')
while setvar(peof,peof-1) >= 0 do
input progname <*x
version !progname >*y
setvar veof
finfo('*y','eof')
while
setvar(veof,veof-1) >= 0 do
input vrec <*y
if
pos("CAPABILITIES:",vrec)=1 or pos("CAP:",vrec)=1
then
setvar veof
0
if
pos("PM",xword(vrec,':')) > 0 then
echo
!progname has PM capabilty
endif
endif
endwhile
endwhile
|
|
38
|
- Let’s add some comments in the beginning and accept a parameter so the
user can specify which files they are interested in.
- Let’s also start adding some error handling
- PARM fileset=@.@.@
# Reports NM and CM program files which have PM capability. Since
two
# LISTFILEs are done to get the full list of NMPRG and PROG files
the final output
# will not be in alphabetic order. Note: HFS syntax is not supported by
VERSION.
purge progf
purge versf
build progf;msg;rec=-80,,f,ascii
build versf;msg;rec=-80,,f,ascii
file x=progf,old
file y=versf,old
continue
listfile !fileset, 6;seleq=[code=NMPRG] >*x
continue
listfile !fileset, 6;seleq=[code=PROG] >>*x
...
|
|
39
|
- Let’s add some real error handling and make the output more user
friendly
- PARM fileset=@.@.@
# (same comments in the beginning as previous version...)
purge progf >$null
purge versf >$null
(same BUILD and FILE eq as before...)
errclear
continue
listfile !fileset, 6;seleq=[code=NMPRG] >*x
if hpcierr <> 0 then
echo !hpcierrmsg
return
endif
continue
listfile !fileset, 6;seleq=[code=PROG] >>*x
if hpcierr <> 0 then
ditto...
...
|
|
40
|
- Let’s try to get the error handling nailed...
- ...
errclear
continue
listfile !fileset,6;seleq=[code=NMPRG] >*x
if hpcierr > 0 then
# print progf which
contains the error
print *x
return
elseif hpcierr < 0 then
# hide warning and erase the contents
of progf (the warn text).
print *x
>$null
errclear
endif
continue
listfile !fileset,6;seleq=[code=PROG] >>*x
if hpcierr > 0 then
# got an error, maybe
the progf file is full? Cannot
display progf as
# above since it could
contain NMPRG files. Also cannot print a subset of
# progf since FPOINT
fails on MSG files.
echo !hpcierrmsg
return
elseif hpcierr < 0 then
# It would be nice to
remove the last two records from progf, but
# since it is a MSG
file we cannot use :PRINT ;start=eof to do this.
# Ignore the warn but
remember the warn text is in progf!
endif
...
|
|
41
|
- PARM fileset=@.@.@
# Reports NM and CM program files which have PM capability. Since
two LISTFILEs
# are done to get the full list of NMPRG and PROG files the final
output will
# not be in alphabetic order.
Note: HFS syntax is not supported by VERSION.
- if word(fsyntax('!fileset')) = "POSIX" then
echo POSIX syntax names
are not supported by the VERSION utility
return
endif
# build the MSG files to hold LISTFILE and VERSION output
purge progf >$null
purge versf >$null
build progf;msg;rec=-80,,f,ascii
build versf;msg;rec=-80,,f,ascii
file x=progf,old
file y=versf,old
- # first list NM program files to a MSG file
errclear
continue
listfile !fileset,6;seleq=[code=NMPRG] >*x
if hpcierr > 0 then
# print progf which
contains the error
print *x
return
elseif hpcierr < 0 then
# hide warning and
erase the contents of progf (the warn text).
print *x
>$null
errclear
endif
- ...
|
|
42
|
- # Now append CM program files to the same MSG file (progf).
# This means that the output will not be in alphabetic
order!
continue
listfile !fileset,6;seleq=[code=PROG] >>*x
setvar peof finfo('*x','eof')
if hpcierr > 0 then
# got an error, maybe
the progf file is full? Cannot
display progf as
# above since it could
contain NMPRG files. Also cannot print a subset of
# progf since FPOINT
fails on MSG files.
echo !hpcierrmsg
return
elseif hpcierr < 0 then
# It would be nice to
remove the last two records from progf, but
# since it is a MSG
file we cannot use :PRINT ;start=eof to do this.
# Ignore the warn but
remember the warn text is in progf!
setvar peof peof-2
endif
- echo
echo The following programs (out of !peof) have PM capability:
echo
setvar pcnt 0
errclear
- (... the read WHILE loop follows...)
|
|
43
|
- # read the combined LISTFILE,6 output and pass each filename to
VERSION
while setvar(peof,peof-1) >= 0 do
input progname
<*x
setvar progname rtrim(progname)
continue
version !progname
>*y
if hpcierr = 0 then
setvar veof
finfo('*y','eof')
while
setvar(veof,veof-1) >= 0 do
input vrec
<*y
if
pos("CAPABILITIES:",vrec) = 1 or pos("CAP:",vrec) =
1 then
setvar veof
0
if
pos("PM",xword(vrec,':')) > 0 then
echo !progname
setvar
pcnt pcnt+1
endif
endif
endwhile
endif
endwhile
echo
echo !pcnt programs have PM
|
|
44
|
- :progcap @.@.vance
- The following programs (out of 22) have PM capability:
- LARSPING.PUB.VANCE
- LINKEDDB.PUB.VANCE
- MOVER.PUB.VANCE
- RYDER.PUB.VANCE
- SWINVENP.PUB.VANCE
- JINFO.TEST.VANCE
- JOBINFO.TEST.VANCE
- SIUDBP.TMP.VANCE
- SIUDBP.TMP1.VANCE
- SIUDBP.TMP2.VANCE
- SIUDBP.UDCS.VANCE
- 11 programs have PM
|
|
45
|
- We start off with some simple, but perhaps still novel examples.
- A few more complex examples are given with emphasis on techniques for
getting more out of MPE.
- There are many more examples at the end of the Appendix.
- Many of the longer examples are on Jazz
- http://jazz.external.hp.com/src/scripts/
|
|
46
|
- display last N records of a file (no process creation)
- PARM file, last=12 “Tail” script
print !file; start= -!last
- display CI error text for a CI error number
- PARM cierr= !cierror “Cierr” script
setvar save_err
cierror
setvar cierror
!cierr
showvar HPCIERRMSG
setvar cierror
save_err
deletevar save_err
- alter priority of job just streamed -- great for online compiles ;-)
- PARM job=!HPLASTJOB; pri=CS “Altp” script
altproc job=!job; pri=!pri
|
|
47
|
- PARM fileset=./@ “LF”
listfile !fileset,6
- PARM group=@ “LG”
listgroup !group; format=brief
- PARM user=@ “LU”
listuser !user; format=brief
- PARM dir=./@ “LD”
setvar _dir “!dir”
if delimpos(_dir, “./”) <> 1 then
# convert MPE name to
POSIX name
setvar _dir dirname( fqualify(_dir)) + “/” + basename(_dir)
endif
listfile !_dir, 6; seleq=[object=HFSDIR] ;tree
|
|
48
|
- PRINTSP script:
- PARM job=!HPLASTJOB
# Prints spoolfile for a job, default is the last job you
streamed
if “!job” = “”
then
echo No job to print
return
endif
setvar hplastjob “!job”
if hplastspid = “”
then
echo No $STDLIST spoolfile to print for “!job”.
return
endif
print !HPLASTSPID.out.hpspool
- :stream scopejob
#J324
:printsp
:JOB SCOPEJOB,MANAGER.SYS,SCOPE.
Priority = DS; Inpri = 8;
Time = UNLIMITED. . .
|
|
49
|
- UPS configuration file, UPSCNFIG.PUB.SYS):
- Contents:
powerfail_message_routing = all_terminals
powerfail_low_battery
= keep_running
powerfail_command_file
= prodshut.opsys.sys
powerfail_grace_period
= 300
- PRODSHUT.OPSYS.SYS script example:
- warn @; Powerfail detected by
UPS. Orderly shutdown BEGIN…
warn @; ***** Please logoff immediately! *****
if jobcnt(“prod1J,usr.acct”, jobID) > 0 then
stream hipriJ
pause 60;
job=!hplastjob
abortjob !jobID
endif
errclear
pause 180; job=@s
if cierror = 9032 then
warn @;System going
down in 2 minutes!
pause 120
endif
shutdown
|
|
50
|
- ANYPARM cmd
# Script that executes a command in a remote session and returns
the
# CIERROR and HPCIERR values for that command back to the
local
# environment.
purge rmstatus; temp >$null
build rmstatus;rec=-80,,f,ascii; temp
remote file rmstatus=rmstatus:$back,oldtemp
continue
remote !cmd
remote echo setvar cierror !!cierror >*rmstatus
remote echo setvar hpcierr !!hpcierr >>*rmstatus
xeq rmstatus
echo remote CIERROR=!cierror, remote HPCIERR=!hpcierr
- :rem listfile 4abc,2
First character in file name not alphabetic.(CIERR 530)
remote CIERROR=530, remote HPCIERR=530
|
|
51
|
- !JOB jobZero,…
!limit +2
!stream job1
!pause job=!hplastjob
!stream job2
!errclear
!pause 600,
!hplastjob
!if hpcierr = -9032 then
! tellop Job
”!hplastjob” has exceeded the 10 minute limit
! eoj
!endif
!stream job3
!pause job=!hplastjob; WAIT
!input reply, “’Reply ‘Y’
for !hplastjob”; readcnt=1; CONSOLE
!if dwns(reply) = “y” then
. . .
|
|
52
|
- setvar x 0
while setvar(token,
&
word(“!hppath”,”,;
“,setvar(x, x+1))) <> ”” do
if
delimpos(token,”/.”) = 1 then
# we have a POSIX
path element
else
# we have an MPE
path element
endif
endwhile
- Why was HPPATH explicitly referenced?
|
|
53
|
- :where @sh@
- SHOWME USER UDC in
SYS52801.UDC.SYS
SH SYSTEM UDC in
HPPXUDC.PUB.SYS
SH.PUB.VANCE NMPRG
SHOWVOL.PUB.VANCE script
BASHELP.PUB.SYS PROG
HSHELL.PUB.SYS script
PUSH.SCRIPTS.SYS script
RSH.HPBIN.SYS NMPRG
SH.HPBIN.SYS NMPRG
/bin/csh NMPRG
/bin/ksh symlink -->
/SYS/HPBIN/SH
/bin/remsh symlink -->
/ENM/PUB/REMSH
/bin/rsh symlink -->
/SYS/HPBIN/RSH
/bin/sh symlink -->
/SYS/HPBIN/SH
|
|
54
|
- CI limits
- Recent CI enhancements
- Redo/do features
- COMMAND and HPCICOMMAND inrtrinsics
- More on UDCs and scripts
- More on CI variables, including compound variables and “arrays”
- Expressions, JINFO, JOBCNT, and PINFO CI functions
- More on I/O redirection
- More examples...
|
|
55
|
- command buffer 511 bytes
- applies to interactive, batch, UDCs, scripts, COMMAND and HPCICOMMAND
intrinsics, NM and CM
- CM command parms limited to 255 bytes due to MYCOMMAND intrinsic, eg.
info= string
- nested IFs and WHILEs 100
- nested UDCs and scripts 30 each
- length of string variable value 1024 bytes
- length of CI variable name 255 bytes
- max number of CI variables 10,800 (approx)
- typical number of CI variables 8,300 (approx)
- length of UDC name 16 bytes
- length of script name 255 bytes
- max number of UDC/script parms 255
- length of user function name* 255 bytes
|
|
56
|
- extended POSIX filename characters
- new CI functions: anyparm, basename, dirname, fqualify, fsyntax, jobcnt,
jinfo, pinfo, wordcnt, xword
- new CI variables: hpdatetime, hpdoy, hphhmmssmmm, hpleapyear, hpmaxpin,
hpyyyymmdd
- new CI commands: abortproc, newci, newjobq, purgejobq, shutdown
- enhanced commands: INPUT from console, FOS store-to-disk, :SHOWVAR to see another job/sessions’
variables, :COPY to= a directory, :ALTJOB HIPRI and jobq=, :LIMIT +-N
- :HELP shows all CI variables, functions, ONLINEINFO, NEW
- user functions, e.g. if myFunc(
a, true,10) > b then ...
|
|
57
|
- delete a word
- delete up to a special character
- delete to end-of-line
- delete two or more non-adjacent characters
- upshift/downshift a character or word
- ^, ^w, v, vw, >^, >v, ^>, v>
- append to end-of-line
- replace starting at end of line
- change one string to another
- undo last or all edits
- available in CI, VOLUTIL, STAGEMAN, DEBUG others...
|
|
58
|
- COMMAND is a programmatic system call (intrinsic)
syntax: COMMAND (cmdimage, error, parm)
- implemented in native mode (NM, PA-RISC mode)
- use COMMAND for system level services, like:
- building, altering, copying purging a file
- no UDC search (a UDC cannot intercept “cmdimage”)
- no command file or implied program file search
- returns command error number and error location
(for positive parmnum), or file system error number for negative parmnum
|
|
59
|
- HPCICOMMAND is an intrinsic
syntax: HPCICOMMAND (cmdimage,error,parm [,msglevel])
- implemented in native mode (NM, PA-RISC mode)
- use HPCICOMMAND for a “window” to the CI, e.g.:
- providing a command interface to a program, “:cmdname”
- UDCs searched first
- command file and implied program files searched
- returns command error number and error location or file system error
number.
- Msglevel controls CI errors/warnings -- similar to the HPMSGFENCE
variable
|
|
60
|
- option logon
- UDCs only (a script can be executed from an “option logon” UDC)
- logon UDCs executed in this order:
- 1. System level 2. Account
level 3. User level
(opposite of the non-logon execution order!)
- CI command search order:
- A. UDCs ( 1. User level 2.
Account level 3. System level)
- thus UDCs can override built-in commands
- B. built-in MPE commands, e.g. LISTFILE
- C. script and program files.
HPPATH variable used to qualify unqualified filenames
- :XEQ command allows script to be same name as UDC or built-in command,
e.g. :xeq listf.scripts.sys
|
|
61
|
- performance
- logon time:
9 UDC files, 379 UDCs, 6050 lines: 1/2
sec.
most overhead in opening and cataloging the UDC files
- to make logons faster remove unneeded UDCs
- execution time:
identical (within 1 msec) for simple UDCs vs scripts,
however:
- factorial script:
:fac 12 157 msec
- factorial UDC (option recursion):
:facudc 12 100 msec
- file close logging impacts performance for scripts more since they are
opened/closed for each invocation
|
|
62
|
- maintenance / flexibility / security
- SETCATALOG opens UDC file, cannot edit without un-cataloging file, but
difficult to accidentally purge UDC file
- UDC commands grouped together in same file, easier to view and organize
- UDC file can be lockword protected but users don’t need to know
lockword to execute a UDC
- scripts opened while being executed (no cataloging), can be purged and
edited more easily than UDCs
- scripts can live anywhere on system.
Convention is to place general scripts in a common location that
grants read or eXecute access to all, e.g. “XEQ.SYS” group
- if script protected by lockword then it must be supplied each time the
script is executed
|
|
63
|
-
File:UDCUSER.udc.finance
- 1. Invoke UDCC, which calls UDCA with
the argument “ghi”
- 2. UDCA is found, starting after the UDCC
definition (option NOrecursion default)
- 3. The line “p1=ghi” is echoed
- 4. Invoke UDCB, which calls UDCA passing
the arg “def”. The recursion option causes
the first UDCA to be found. This calls
UDCC and follows the path at step 1
above
- 5. The line “p1=def” is echoed
|
|
64
|
- scripts and programs are searched for after the command is known not to
be a UDC or built-in command
- same order for scripts and for program files
- fully or partially qualified names are executed without qualification
- unqualified names are combined with HPPATH elements to form qualified
filenames:
- first match is executed – could be a script, could be a program file
- filecode = 1029, 1030 for program files
- EOF > 0 and filecode in 0..1023 for script files
- to execute POSIX named scripts with HPPATH qualification, a POSIX named
directory must be present in HPPATH
|
|
65
|
- filename:
AUDC.PUB.SYS
header:
body:
end-of-UDC:
- header:
body:
|
|
66
|
- filename:
PRNT.SCRIPTS.SYS
header:
- body:
eof
filename:
LG.SCRIPTS.SYS
- header:
body:
|
|
67
|
- all CI variables are job/session global, except the following:
HPAUTOCONT, HPCMDTRACE, HPERRDUMP, HPERRSTOLIST, HPMSGFENCE,
which are local to an instance of the CI
- thus it is easy to set “persistent” variables via a logon UDC
- need care in name of UDC and script “local” variables to not collide
with existing job/session variables
- _scriptName_varname -- for all script variable names. Use:deletevar
_scriptName_@ at end of script
- Can create unique variable names by using !HPPIN, !HPCIDEPTH,
!HPUSERCMDEPTH as part of the name, e.g.
:setvar
_script_xyz_!hppin , value
- save original value of some “environment” variables
- :setvar
_script_savemsgfence
hpmsgfence
:setvar hpmsgfence 2
|
|
68
|
- two ways to reference a variable:
- explicit -- !varName
- implicit -- varName
- some CI commands expect variables (and expressions) as their arguments,
e.g.
- :CALC, :IF, :ELSEIF, :SETVAR, :WHILE
- use implicit referencing here, e.g.
:if (HPUSER = “MANAGER”) then
- most CI commands don’t expect variable names (e.g. BUILD, ECHO, LISTF)
- use explicit referencing here, e.g.
:echo You are logged on as: !HPUSER.!HPACCOUNT
- note: all UDC/script parameters must be explicitly referenced
- all CI functions accept variable names, thus implicit referencing works
- :while JINFO (HPLASTJOB, “exists”) do… better than ...
:while JINFO (“!HPLASTJOB”, “exists”) do
|
|
69
|
- processed by the CI early, before command name is known
- can cause hard-to-detect bugs in scripts - array example
- lose variable type -- strings need to be quoted, e.g.. “!varName”
- !! (two exclamation marks) used to “escape” the meaning of “!”, multiple
“!’s” are folded 2 into 1
- even number of “!” --> don’t reference variable’s value
- odd number of “!” -->
reference the variable’s value
- useful to convert an ASCII number to an integer, e.g.
setvar int “123” or
input foo, “enter a number”
if !int > 0 then … if !foo = 321 then ...
- the only way to reference UDC or
script parameters
- the only way for most CI commands to reference variables
|
|
70
|
- evaluated during the execution of the command -- later than explicit
referencing
- makes for more readable scripts
- variable type is preserved -- no need for quotes, like: “!varname”
- only 5 commands accept implicit referencing: CALC, ELSEIF, IF, SETVAR, WHILE -- all others require
explicit referencing
- all CI function parameters accept implicit referencing
- variables inside ![expression] may be implicitly referenced
- performance differences:
- “!HPUSER.!HPACCOUNT” = “OP.SYS” 4340 msec
- HPUSER + “.” + HPACCOUNT = “OP.SYS” 4370 msec
- HPUSER = “OP” and HPACCOUNT = “SYS” 4455 msec*
(*with user match true)
- I prefer the last choice since
many times :IF will not need to evaluate the expression after the AND
|
|
71
|
- :setvar a “!!b” # B is not referenced, 2!’s fold to 1
- :setvar b “123”
- :showvar a, b A=“!b” B=123
- :echo b is !b, a is !a b is 123, a is 123
- :setvar a123 “xyz”
- :echo Compound var "a!!b": !"a!b” Compound var "a!b":
xyz
- :setvar J 2
:setvar VAL2 “bar”
:setvar VAL3 “foo”
- :calc VAL!J bar
- :calc VAL![J] bar
- :calc VAL![decimal(J)] bar
- :calc VAL![setvar(J,J+1)] foo
|
|
72
|
- simple convention using standard CI variables
- varname0 = number of elements in the array
varname1…varnameN = array elements, 1 .. !varname0
varname!J = name of element J
!”varname!J” = value of element J
- :showvar buffer@
- BUFFER0 = 6
BUFFER1 = aaa
BUFFER2 = bbb
BUFFER3 = ccc
BUFFER4 = ddd
BUFFER5 = eee
BUFFER6 = fff
|
|
73
|
- centering output:
- PARM count=5 “Center” script
setvar cnt 0
while setvar(cnt,cnt+1) <= !count do
setvar string!cnt,input("Enter
string !cnt: ")
endwhile
setvar cnt 0
while setvar(cnt,cnt+1) <= !count do
echo ![rpt("
",39-len(string!cnt))]!"string!cnt”
endwhile
- :center
- Enter string 1: The great thing about Open Source
Enter string 2: software is that you can
Enter string 3: have any color
Enter string 4: "screen of death”
Enter string 5: that you want.
- The great thing about Open Source
software is that you
can
have any color
"screen of
death”
that you want.
|
|
74
|
- example 1: # array name is “rec”
setvar j 0
setvar looping true
while looping do
input name, “Enter
name “
if name = “” then
setvar looping
false
else
setvar j j+1
setvar rec!j name
endif
endwhile
setvar rec0 j
- :xeq exmpl1
- infinite loop!, won’t end until <break>
|
|
75
|
- example 2:
setvar j 0
setvar looping true
while looping do
setvar
NAME “”
input name, “Enter
name “
if name = “”
then
setvar looping
false
else
setvar j j+1
setvar
rec!j name
endif
endwhile
setvar rec0 j
- :xeq exmpl2 <datafile (datafile
has 20 text records)
(“enter name” prompt shown 20 times snipped…)
End of file on input. (CIERR 900)
input name, "enter
name “
Error executing commands in WHILE loop. (CIERR 10310)
|
|
76
|
- example 3:
setvar j 0
if HPINTERACTIVE then
setvar prompt “’Name = ‘”
setvar limit 2^30
setvar
test ‘name= “” ‘
else
setvar prompt “”
setvar limit FINFO
(HPSTDIN, ”eof”)
setvar
test “false”
endif
while (j <
limit) do
setvar name “”
input name , !prompt
if !test
then
setvar limit 0 #
exit interactive input
else
setvar j
j+1
setvar rec!j name
endif
endwhile
setvar rec0 j
|
|
77
|
- :xeq exmpl3 <datafile
- :showvar rec@
REC1 = line1
REC2 = line2
…
REC20 = line20
REC0 = 20
- performance:
- Script as is: 100 records: 530
millisecs
- Script modified for file input only (shown in notes):
100 records: 380 millisecs
|
|
78
|
- can we fill arrays (and read files) faster?
- example 4:
setvar rec0 0
setvar limit FINFO (HPSTDIN, ”eof”)
while setvar(rec0, rec0+1) <= limit and &
setvar(rec![rec0+1],
input()) <> chr(1) do
endwhile
setvar rec0 rec0-1
- performance (:xeq exmpl4
<datafile):
- 100 records: 185 millisecs (twice
as fast!)
|
|
79
|
- operators:
- + (ints and strings), -, *, /, ^, (), <, <=, >, >=, =, AND,
BAND, BNOT, BOR, BXOR, CSL, CSR, LSL, LSR, MOD, NOT, OR, XOR
- precedence (high to low):
- 1) variable dereferencing
- 2) unary + or -
- 3) bit operators (csr, lsl…)
- 4) exponentiation ( ^ )
- 5) *, /, mod
- 6) +, -
- 7) <, <=, =, >, >=
- 8) logical operators (not, or…)
- left to right evaluation, except
exponentiation is r-to-l
|
|
80
|
- syntax: JINFO (“[#]S|Jnnnn”,
“item” [,status] )
where jobID can be “[#]J|Snnn” or “0”, meaning “me”
- 63 unique items: Exists, CPUSec, IPAddr, JobQ, Command,
JobUserAcctGroup, JobState, StreamedBy, Waiting ...
- status parm is a variable name.
If passed, CI sets status to JINFO error return -- normal CI
error handling bypassed
- can see non-sensitive data for any job on system
- can see sensitive data on: “you”; on other jobs w/ same
user.acct if jobsecurity is LOW; on other jobs in same
acct if AM cap; on any job if SM or OP cap
|
|
81
|
- syntax: JOBCNT (“job_spec”
[,joblist_var] )
- “Job_Spec” can be:
- “user.account”
- “jobname,user.account”
- “@J”, “@S”, “@”
- “@J:[jobname,]user.acct” or “@S:[jobname,]user.acct”
- wildcarding is supported
- use empty jobname (“,”) to select jobs without jobnames
- omit jobname to match any jobname
|
|
82
|
- syntax: PINFO (pin, “item”
[,status] )
where PIN can be a string, “[#P]nnn[.tin]”, or a simple integer,
“0” is “me”
- 66 unique items: Alive, IPAddr, Parent, Child, Children, Proctype,
WorkGroup, SecondaryThreads, NumOpenFiles, ProgramName, etc.
- status parm is a variable name.
If passed, CI sets status to PINFO error return -- normal CI
error handling bypassed
- can see non-sensitive data for any user process on system
- follows SHOWPROC’s rules for sensitive data
|
|
83
|
- > name - redirect output from
$STDLIST to “name”
- “name” will be overwritten if it already exists
- file will be saved as “name”;rec=-256,,v,ascii;disc=10000;TEMP
- file name can be MPE or POSIX syntax
- >> name - redirect, append
output from $STDLIST to “name”
- same file attributes for “name” if it is created
- < name - redirect input from
$STDIN to “name”
- “name” must exist (TEMP files looked for before PERM files)
- I/O redirection has no meaning if the command does not do I/O to $STDIN
or $STDLIST
- available on all commands, except:
- IF, ELSEIF, SETVAR, CALC, WHILE, COMMENT, SETJCW, TELL, TELLOP, WARN.
|
|
84
|
- how it works:
- CI ensures the command is not one of the excluded commands
- CI scans the command line looking for <, >, >> followed by
a possible filename (after explicit variable resolution has already
occurred)
- text inside quotes is excluded from this scan
- text inside square brackets is excluded from this scan
- filename is opened and “exchanged” for the $STDIN or $STDLIST
- after the command completes the redirection is undone
- examples:
- INPUT varname < filename
- ECHO The next answer is:
!result >>filename
- LISTFILE ./@,6 > filename
- PURGEACCT myacct <Yesfile
- PURGE foo@ ;temp ;noconfirm >$null
- ECHO You need to include !<THIS!>
too!
|
|
85
|
- Assume variable X = "ab c;de,,fg;hij=k lmn,op=qr” and 500 iterations for timing tests
- Parse out all tokens in a string variable:
- setvar j 0
while j<= len(x) do
setvar tok word(x, , , j, j+1)
endwhile 2136 millisecs
- OR
- setvar j 0
while setvar(j, j+1) <= wordcnt(x) do
setvar tok word(x, , j)
endwhile 2298 msecs
- OR
- setvar j 0 # fails on null token
while setvar(tok, word(x, , setvar(j, j+1))) <= “” do
endwhile 1686 msecs
|
|
86
|
- Assume variable X = "ab c;de,,fg;hij=k lmn,op=qr”
- Extract the first N tokens from a string var
- setvar toks lft(x, delimpos(x, , N) -1) # includes all token delimiters
- OR
- setvar j 0 # original delimiters replaced by single space
setvar toks “”
while setvar(j, j+1) <= N do
setvar toks toks + word(x, , j) + “ “
endwhile
- Extract the last N tokens from a string var
- setvar toks rht(x, -delimpos(x, , -N)-1) # includes all token
delimiters
- OR
- setvar j 0 # original delimiters replaced by single space
setvar toks “”
while setvar(j, j+1) <= N do
setvar toks word(x, , -j) + “ “ + toks
endwhile
|
|