|
|
|
… and other tidbits
presented
by
Jeff Vance, HP-CSY
jeff_vance@hp.com |
|
|
|
|
UDCs and scripts |
|
parameters |
|
variables |
|
entry points |
|
expressions and functions |
|
i/o redirection and file I/O |
|
error handling |
|
script cleanup techniques |
|
debugging and good practices |
|
lots of examples |
|
appendix |
|
|
|
|
IF,
ELSEIF, ELSE, ENDIF branching
ESCAPE, RETURN |
|
WHILE,
ENDWHILE looping |
|
ECHO,
INPUT terminal, console, 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 |
|
|
|
|
|
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 |
|
|
|
|
|
|
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 HPPATH 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 |
|
|
|
|
|
|
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 |
|
|
|
|
|
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 |
|
|
|
|
|
|
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 |
|
|
|
|
|
|
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 |
|
|
|
|
|
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 |
|
|
|
|
|
|
|
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 |
|
|
|
|
|
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 |
|
|
|
|
filename: AUDC.PUB.SYS
header:
body:
end-of-UDC |
|
header:
body: |
|
|
|
|
filename: PRNT.SCRIPTS.SYS
header: |
|
body:
eof
filename:
LG.SCRIPTS.SYS |
|
header:
body: |
|
|
|
|
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 |
|
|
|
|
|
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 |
|
|
|
|
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 |
|
|
|
|
|
|
|
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 |
|
|
|
|
simple convention for executing 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 |
|
|
|
|
|
|
|
two approaches for alternate entries: |
|
define a parm to be the entry point name,
defaulting to the main part of the code (“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 |
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
i/o redirection specific approach: |
|
PARM p1
… # no “entry” parm defined |
|
if HPSTDIN = “$STDIN” then |
|
…
(“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 |
|
|
|
|
|
|
|
113 predefined “HP” variables |
|
user can create 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 |
|
|
|
|
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. |
|
|
|
|
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 |
|
|
|
|
|
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 |
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
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 |
|
|
|
|
|
|
|
: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 |
|
|
|
|
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 |
|
|
|
|
|
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. |
|
|
|
|
|
|
|
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> |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
: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 |
|
|
|
|
|
|
|
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!) |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
what is an expression? |
|
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”) |
|
partial evaluation:
if true or x # “x” side not
evaluated
if false and
x # “x” side not evaluated
if bound(z) and z
> 10 then # if “z” not defined it won’t be referenced |
|
problems when MPEX runs the script |
|
where can expressions be used? |
|
5 commands that accept implicit variable
references:
:calc, :if, :elseif, :setvar, :while |
|
![ expression ]
can be used in any command:
:build afile; rec=-80; disc= ![100+varX]
:build bfile; disc= ![
finfo(“afile”,”eof”)*3] # file b is 3 times bigger |
|
examples: |
|
:print ![input(“File name? “)] |
|
:setvar reply ups(rtrim(ltrim(reply))) |
|
|
|
|
|
|
|
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 |
|
ABS, MAX, MIN, MOD, ODD |
|
job/process functions: |
|
JINFO, JOBCNT, PINFO |
|
misc. functions: |
|
ANYPARM, BOUND, INPUT, SETVAR, TYPEOF |
|
|
|
|
|
|
|
|
|
|
|
> 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. |
|
|
|
|
|
|
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 the
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! |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
why not use INPUT in WHILE to read a flat file?,
e.g.: |
|
while
not eof do
input varname < filename
endwhile |
|
answer: the CI opens and closes “filename” each
iteration, thus you will be reading the 1st record over and
over… |
|
|
|
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 file works because each read is destructive,
so next INPUT reads next record |
|
|
|
|
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 |
|
|
|
|
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 |
|
|
|
|
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) |
|
|
|
|
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 24 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! |
|
|
|
|
|
use HPAUTOCONT variable judiciously : |
|
better --
continue
command
if hpcierr > 0 then ... |
|
if error-condition 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 |
|
|
|
|
|
|
|
|
|
delete variables “local” to the UDC / script |
|
:deletevar _”prefix”_@ |
|
purge scratch files |
|
reset “local” file equations |
|
don’t do the above if still debugging! |
|
better, 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 |
|
|
|
|
|
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 IO 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 (e.g., 2 eof counters),
reading from terminal but $stdin is already redirected to a file |
|
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, |
|
|
|
|
|
|
|
|
some simple examples to get started |
|
string manipulation and parsing examples |
|
dealing with quotes |
|
… and much more |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
1) parse out all tokens in a string var |
|
2) extract the first N tokens from a string var |
|
3) extract the last N tokens from a string var |
|
4) test for “hi” somewhere in a string var (or
“LOGON” vs. “NOLOGON”) |
|
5) count tokens in a string var |
|
6) remove Nth token from a string var |
|
7) remove N consecutive tokens from a string var |
|
|
|
|
|
|
|
|
|
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 seconds . . . |
|
|
|
|
|
PARM p1="my value",
p2="something“
# create a simple job passing parms and variables to the job
setvar testvar1 true
setvar testvar2 46
setvar testvar3 "abc“
echo
!!job jeff.vance;outclass=,2
>tmpjob
echo !!setvar myP1 "!p1" >>tmpjob
echo !!setvar
myP2 "!p2"
>>tmpjob
echo !!setvar myVar1 !testvar1 >>tmpjob
echo !!setvar
myVar2 !testvar2
>>tmpjob
echo !!setvar myVar3
"!testvar3"
>>tmpjob
echo !!showvar my@ >>tmpjob
echo !!eoj
>>tmpjob
stream tmpjob |
|
|
|
|
|
CD script |
|
PARM dir=“”
setvar d “!dir”
# “-” means go to prior CWD
if d = ‘-’ and bound(save_chdir) then
setvar d save_chdir
elseif fsyntax(d) =
“MPE” then # MPE
syntax?
if finfo(“./”+d,
“exists”) then # HFS dir?
setvar d “./” + d
elseif finfo(“../”+ups(d),
“exists”) then # MPE group?
setvar d “../” + ups(d)
elseif finfo(ups(d), “exists”)
then # MPE dir name?
setvar d ups(d)
endif
endif
setvar save_chdir HPCWD
chdir !d |
|
|
|
|
|
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 |
|
|
|
|
|
|
before: output: |
|
setvar j 0 a xx bbbbbb xx
while setvar(j,j+1)
< 4 do aa xx bbbb xx
setvar a rpt(“a”,
j) aaa xx bb xx
setvar b rpt(“b”,
(4-j)*2)
echo !a xx !b xx
endwhile |
|
after: |
|
while
…
setvar a ; setvar b…same
way… a xx bbbbbb xx
echo !a ![rpt(“
“, 3-len(a))]xx & aa xx bbbb xx
![rpt(“
“, 6-len(b))] !b xx aaa xx bb
xx
endwhile |
|
|
|
|
|
|
PARM vers_parm=!hprelversion “Vers”
script
# react to MPE version string
setvar vers "!vers_parm”
# convert to integer, e.g.. "C.65.02" => 6502
setvar vers str(vers,3,2) + rht(vers,2)
setvar vers !vers
if vers >= 7000
then
echo On 7.0!
elseif vers >= 6500 then
echo On 6.5!
elseif vers >= 6000 then
echo On 6.0!
endif |
|
|
|
|
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 >$null
build rmstatus;rec=-80,,f,ascii
remote
file rmstatus=rmstatus:$back,old
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 |
|
|
|
|
!JOB job0…
!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
. . . |
|
|
|
|
|
ANYPARM info=![""] # “anyrun” script
run volutil.pub.sys; info=”:!info" |
|
:anyrun echo "Hi there!”
run
volutil.pub.sys;info=”:echo "Hi there!""
^
Expected semicolon or carriage return.
(CIERR 687) |
|
ANYPARM info=![""]
setvar _inf repl('!info', '"', '""') #
double up quotes in :RUN
run volutil.pub.sys;info=”:!_inf " |
|
:anyrun echo "Hi there!”
Volume
Utility A.02.00, (C) Hewlett-Packard Co., 1987. All Rights...
volutil: :echo "Hi there!”
"Hi there!” |
|
|
|
is this correct now? |
|
|
|
|
|
ANYPARM info=![""]
setvar _inf anyparm(!info) #
note info parm is not quoted
setvar _inf repl(_inf, '"', '""')
run volutil.pub.sys;info=”:_!inf ” |
|
:anyrun echo "Hi there, ‘buddy’!”
Volume
Utility A.02.00, (C) Hewlett-Packard Co., 1987. All Rights...
volutil: :echo "Hi there, ‘buddy’!”
"Hi there, ‘buddy’!” |
|
|
|
|
|
|
|
PARM varname, minlen=4, maxlen=8
# This script returns in the variable specified as "varname" a
`random’
# name consisting of letters and numbers - cannot start with a number.
# At least "minlen" characters long and not more than
"maxlen" chars.
## expression for a `random' letter:
setvar
letter "chr( (hpcpumsecs mod
26) + ord('A') )”
## expression for a `random' number:
setvar
number "chr((hpcpumsecs mod
10) + ord('0'))"
## first character must be a letter
setvar !varname !letter
## now fill in the rest, must have at least "minlen" chars , up
to "maxlen"
setvar i 1
setvar limit min( (hpcpumsecs
mod !maxlen) + !minlen, !maxlen)
while setvar(i,i+1) <= limit do
if odd(hpcpumsecs)
then
setvar
!varname !varname + !letter
else
setvar
!varname !varname + !number
endif
endwhile |
|
|
|
|
|
|
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 did I explicitly reference HPPATH? |
|
|
|
|
|