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 >
Text File  |  1995-07-09  |  43KB  |  1,011 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.                               PAPER NUMBER 4018
  9.  
  10.  
  11.  
  12.                      Programming the Command Interpreter
  13.  
  14.  
  15.  
  16.                     An Introduction, a Dog, and New Tricks
  17.  
  18.  
  19.  
  20.  
  21.  
  22.  
  23.  
  24.  
  25.  
  26.  
  27.  
  28.  
  29.  
  30.                                by Ken Robertson
  31.                                    May 1995
  32.  
  33.  
  34.  
  35.                               Robelle Consulting
  36.                               15399-102A Avenue
  37.                           Surrey, BC Canada V3R 7K1
  38.  
  39.                                 (604) 582-1700
  40. Introduction
  41.  
  42. An  overworked,  understaffed  data processing department is all too common in
  43. today's ever belt-tightening, down-sizing and de-staffing companies.
  44.  
  45. An ad-hoc request may come to the harried data processing  manager.   She  may
  46. throw her hands up in despair and say, "It can't be done.  Not within the time
  47. frame that you need it in."   Of course, every computer-literate person  knows
  48. deep down in his heart that every programming request can be fulfilled, if the
  49. programmer has enough hours to code, debug, test, document and  implement  the
  50. new  program.   The  informed  DP  manager  knows that programming the Command
  51. Interpreter (CI) can sometimes reduce  that  time,  changing  the  "impossible
  52. deadline" into something more achievable.
  53.  
  54. In  this paper you will learn some of the programming concepts of the CI.  You
  55. will also learn how you can use the CI to solve some  common  data  processing
  56. problems.
  57.  
  58. You will find a short glossary at the end that explains some of the terms used
  59. in this paper.
  60.  
  61. A Little History
  62.  
  63. Before MPE/iX, (and even before MPE/XL), there was a run-time environment  for
  64. the MPE/V class of HP computers called the Command Interpreter.
  65.  
  66. The  command interpreter on MPE/V machines had limited programming capability,
  67. with If /  Else constructs and numeric variables limited to values  between  0
  68. and  65535.   The  basic  interface  of the MPE/V CI (Command Interpreter) was
  69. ported to MPE/iX machines, and beefed up so it would be usable as  a  run-time
  70. shell.
  71.  
  72. The  MPE/iX  command interpreter has a generous command set, pushing the shell
  73. into  the  realm  of  a  true  programming  tool.   Its  ability  to  evaluate
  74. expressions  and to perform I/O on files allows the end-user to perform simple
  75. data-processing functions without having to resort to using a 3GL.  The CI can
  76. be  used  to solve complex problems.  Its code, however, is interpreted, which
  77. may cause a CI solution to a problem  to  execute  too  slowly  for  practical
  78. purposes.   In  this  case  a 3GL, 4GL, or third party software solution would
  79. would have to be investigated.
  80.  
  81. What are Command Files?
  82.  
  83. Command files are a collection of commands in flat files, of  either  variable
  84. or  fixed length record structure, that reside in the MPE or POSIX file space.
  85. Phew!  Basically, command files are what you could call MPE Macros.   Anything
  86. that  you  can  do in the CI interactively, you can do with command files, and
  87. then some.  You can use command files in situations that call  for  repetitive
  88. functions,  such  as  re-compiling source code, special spooler commands, etc.
  89. Command files are also great when you want to hide details from the end-user.
  90.  
  91. A command file is executed when its name is is typed in  the  CI,  or  invoked
  92. from  a  command file or programming shell.  Just as in program execution, the
  93. user's HPPATH is searched to determine the location of the command file.
  94.  
  95. When you're deciding where to store a command file, it's a good idea  to  have
  96. some  conventions  in  mind.   One convention commonly used is to create a CMD
  97. group (short for command group) in every account for local command files,  and
  98. a  production  CMD group that everyone can access.  You can set up a logon UDC
  99. to ensure that the HPPATH variable is set correctly.
  100.  
  101. Hey - why not have a logon command file?  Well, you can't, because the current
  102. version  of  MPE  doesn't  support  it.   If you insist on having one, you can
  103. always make a logon UDC that executes your logon command file.
  104.  
  105.    ***********************
  106.    logonudc
  107.    option logon, nobreak
  108.    logon.cmd.prod
  109.    bye
  110.  
  111. The  above  UDC  is  invoked  at  logon,  and executes the command file called
  112. logon.cmd.prod.  The last line in the command file logs the user off.
  113.  
  114. One question that often arises is "Which are better - command files or UDC's?"
  115. Interestingly  enough,  UDC  stands  for  User Defined Command.  UDC's must be
  116. managed differently from command files, and have small  differences  in  their
  117. internal organization.
  118.  
  119. A  UDC  file  is  a  library  of callable commands with multiple entry points,
  120. whereas a command file has but one entry point.  You manage UDC files via  the
  121. Setcatalog  and  Showcatalog commands.  If you need to make modifications to a
  122. UDC, you must make sure that no users are accessing the file when you want  to
  123. save your changes.
  124.  
  125. UDC  files  must  be attached to a session using the Setcatalog command.  This
  126. means that the UDC file (and all of the UDC's you set up) will be in use while
  127. a  user  is logged on.  In order to make changes to a UDC file,  you will have
  128. to either bump the users accessing the file off the system, or create a  brand
  129. new  UDC  file  and issue a new Setcatalog command.  The changes that you made
  130. will take effect only when the user logs back on again.
  131.  
  132. With command files, on the other hand, you may create new ones  at  any  time.
  133. They  will  be  immediately  accessible to anyone who is logged on.  You still
  134. need exclusive access to delete or to make  changes  to  an  existing  command
  135. file, but users are also less likely to be accessing command files.
  136.  
  137. In  the  current  operating system,  UDC's do tend to be more secure - you can
  138. use OPTION NOHELP to prevent snooping eyes from  reading  the  contents  of  a
  139. particular user command.  [Note:  A future operating system release will allow
  140. XEQ access for command files.  Currently, you must have XEQ and READ access to
  141. the files in order to use them.]
  142.  
  143. The Command File Structure
  144.  
  145. The structure of a command file is as follows:
  146.  
  147.    PARM parameter declarations       (optional)
  148.    MPE command
  149.    .
  150.    .
  151.    .
  152.    MPE command
  153.  
  154. Any  MPE  command  can  be  used.  This includes If /  Else, and While to form
  155. looping constructs.  You can also invoke any command file or UDC by its  name.
  156. Command  files  can be called recursively, nesting levels up to 30 times deep.
  157. (Note to Qedit users:  Qedit has a maximum recursive level of 10, which should
  158. be enough.)
  159.  
  160. The Programming Environment
  161.  
  162. Programmers will tell you that they need an "environment in which to operate".
  163. Development of code  should  progress  smoothly,  with  a  minimum  of  effort
  164. required to translate ideas into working programs.
  165.  
  166. For  this,  you  need  an  editor,  and  in the case of 3GL and 4GL, a program
  167. (compiler) to compile your code.  Strictly speaking, you don't need an  editor
  168. to  create  CI "programs", but it does make your job of writing the code a lot
  169. easier.  Qedit, from Robelle Consulting, is a  flexible  editor  in  which  to
  170. develop  CI  programs.  You can stay inside Qedit to write, keep and test your
  171. command files, saving steps in the traditional "Text, Keep, Exit,  Try  Again,
  172. and Edit" cycle.
  173.  
  174. If  you  don't  want  to  use an editor to create command files, you can use a
  175. feature of the CI called I/O  redirection.   For  example,  the  following  CI
  176. commands will build a simple command file called sj.
  177.  
  178.     echo echo All executing jobs: > sj
  179.     echo showjob;exec  >> sj
  180.  
  181. Note  that as your command files become more complex, you may find it trickier
  182. to edit them in this manner.  Consider a command file that can be used to look
  183. up telephone numbers from a flat file:
  184.  
  185.    anyparm search_string = "?"
  186.    if "!search_string" = "?" then
  187.        echo  command file:  phone  search_string
  188.        return
  189.    endif
  190.    run grep.bin.hpsys "-c !search_string"
  191.  
  192. How  can  you echo variables into a file without getting the assigned value of
  193. the variable? Say, for example the value of the variable my_time was equal  to
  194. "10:02".  The statement
  195.  
  196.    echo echo The time is !mytime > cmdfile
  197.  
  198. would put the string "echo The time is 10:02" into the  first  record  of  the
  199. file.   To  get  the variable name into the file, you must use two exclamation
  200. marks at the beginning of the variable name, like this:
  201.  
  202.    echo echo The time is !!mytime > cmdfile
  203.  
  204. That  would write the string "echo The time is !my_time" into the first record
  205. of Cmdfile.
  206.  
  207. Variables
  208.  
  209. As in any program, you use variables to keep track  of  things.   HP  provides
  210. three  types  of  variables in the CI - numeric, boolean and string.  Numerics
  211. are limited to integers, booleans to true or false, and strings to lengths  up
  212. to 256 characters.  Unfortunately, there is no provision for arrays.  However,
  213. if you really need them, they can be simulated (slowly!) using either files or
  214. strings, or translatable naming conventions.  We'll discuss this a bit later.
  215.  
  216. The  variable  name itself can be a combination of alphanumeric characters and
  217. the underbar character, and the name must start with either  a  letter  or  an
  218. underbar.
  219.  
  220. Setting and Displaying Variables
  221.  
  222. You have two methods for setting variables:  use either the typical assignment
  223. command, or prompt a value from a terminal.
  224.  
  225.    1.  setvar variable_name  expression
  226.    2.  input  variable_name  prompt
  227.  
  228. The expressions for Setvar  can  be  made  up  of  either  numerics,  strings,
  229. booleans,  variable names, or variable de-referencing (sort of like pointers),
  230. all glued together with operators.  For example, to concatenate the values  of
  231. two string variables, you can do the following:
  232.  
  233.   setvar  group_account  "!hpgroup" + "." + "!hpaccount"
  234.  
  235. If  the  logon  group  and  account  were PUB and SYS, the above would set the
  236. variable group_account to the value of PUB.SYS.  Figure 1 shows a list of  all
  237. of the operators possible in a Setvar command.
  238.  
  239.        Logical operators:             AND, OR, XOR, NOT
  240.        Boolean functions and values:  BOUND, TRUE, FALSE, ALPHA, ALPHANUM, NUMERIC, ODD
  241.        Comparison operators:          =, <>, <, >, <=, >=
  242.        Bit manipulation operators:    LSL, LSR, CSR, CSL, BAND, BOR, BXOR, BNOT
  243.        Arithmetic operators:          MOD, ABS, * , / , + , -, ^ (exponentiation)
  244.        Functions returning strings:   CHR, DWNS, UPS, HEX, OCTAL, INPUT, LFT, RHT, RPT,
  245.                                       LTRIM, RTRIM, STR
  246.        Functions returning integers:  ABS, LEN, MAX, MIN, ORD, POS, TYPEOF
  247.        Other functions:               FINFO, SETVAR
  248.  
  249.                          figure 1.  Setvar Operators
  250.  
  251. Variable Locality and Longevity
  252.  
  253. Although some variables seem permanent, only a few  system-supplied  read-only
  254. variables  are  permanent.   For  example,  the  HPSUSAN variable contains the
  255. special CPU number  unique  to  each  HP  3000/iX  computer.   All  HP-defined
  256. variables  start  with the letters HP, and appear in uppercase.  To see a list
  257. of them, simply enter the command
  258.  
  259.    showvar hp@
  260.  
  261. User-variables - the  variables  that you create - can have a lifetime as long
  262. as your session, but  no  longer.   You  create  variables  using  the  Setvar
  263. command, and delete them using the Deletevar command.
  264.  
  265. CI  variables  are  global to your session.  If you create a variable and then
  266. start up a new CI shell, the variables will be accessible from the  new  shell
  267. as  well.  You do have the ability to create local variables, which exist only
  268. during the execution of a command file.  You define local variables  with  the
  269. parm line in the command file.  For example,
  270.  
  271.   parm local_myvar="Not Available In Stores"
  272.   echo !local_myvar
  273.  
  274. If  after  executing the above command file, you do a showvar local_myvar, the
  275. variable will not exist, and a CI error will grace your screen.  The temporary
  276. variable my_var existed only during the execution of the command file.
  277.  
  278. Local  variables cannot be modified, and are accessed only by reference within
  279. the command files.  If you have a local variable and a permanent variable with
  280. the  same  name,  then  the  local variable takes precedence during expansion.
  281. Make sure that your local variables have different names from  your  permanent
  282. ones,  otherwise  you'll  have more confusion than you need at debugging time.
  283. The  following  command  file  illustrates  local  versus  permanent  variable
  284. referencing.
  285.  
  286. hithere
  287.   parm hpjobname="ZZTOP"
  288.   echo Good evening, !hpjobname.
  289.   showvar hpjobjname
  290.  
  291. Exexcuting Hithere gives us
  292.  
  293.   Good evening, ZZTOP
  294.   HPJOBNAME = KEN
  295.  
  296. Attention System Managers:  Don't worry! This trick won't fool  programs  that
  297. retrieve  the  value  of  variables using the HPCIGETVAR intrinsic, or command
  298. files that are nested within a command file.  The value of the local  variable
  299. will  be  returned  during expansion only within the command file that defines
  300. it.
  301.  
  302. Getting Data Into and Out of Files
  303.  
  304. So you want to keep some data around for awhile?  Use a file!  Well, you  knew
  305. that  already,  I'll  bet.   What you probably didn't know is that you can get
  306. data into and out of files fairly easily, using I/O re-direction and the print
  307. command.   I/O  re-direction  allows  input or output to be directed to a file
  308. instead of to your terminal.  I/O re-direction uses the symbols ">", ">>"  and
  309. "<".  Use ">" to re-direct output to a temporary file.  (You can make the file
  310. permanent if you use a file command.)  Use ">>" to append output to the  file.
  311. Finally, use "<" to re-direct input from a file.
  312.  
  313.   echo Value 96 > myfile
  314.   echo This is the second line >> myfile
  315.   input my_var < myfile
  316.   setvar mynum_var str("!my_var",7,2)
  317.   setvar mynum_var_2 !mynum_var - (6 * 9 )
  318.   echo The answer to the meaning of life, the universe
  319.   echo and everything is !mynum_var_2.
  320.  
  321. After executing the above command file,  the  file  Myfile  will  contain  two
  322. lines, "Value 42" and "This is the second line".  (Without quotes, of course.)
  323. The Input command uses I/O re-direction to read the first record of the  file,
  324. and  assigns  the value to the variable my_var.  The first Setvar extracts the
  325. number from the middle of the string, and proceeds to  use  the  value  in  an
  326. important calculation in the next line.
  327.  
  328. How  can  you  assign the data in the second and consequent lines of a file to
  329. variables?  You use the Print command to select the record that you want  from
  330. the file, sending the output to a new file.
  331.  
  332.   print myfile;start=2;end=2 > myfile2
  333.  
  334. You can then use the Input command to extract the string from the second file.
  335.  
  336. Rolling Your Own System Variables
  337.  
  338. It's  easy enough to create a static file of Setvar commands that gets invoked
  339. at logon time, and it's not difficult to  modify  the  file  programmatically.
  340. For  example,  let's say that you would like to remember a particular variable
  341. from session to session, such as the name of your favorite printer.   You  can
  342. name the file that contains the Setvars, Mygvars.  It will contain the line:
  343.  
  344.    setvar my_printer "biglaser"
  345.  
  346. The value of this variable may change during your session, but you may want to
  347. keep  it for the next time that you log on.  To do this, you must replace your
  348. normal logoff procedure (the Bye or Exit command) with  a  command  file  that
  349. saves the variable in a file, and then logs you off.
  350.  
  351. byebye
  352.    purge mygvars > $null
  353.    file mygvars;save
  354.    echo setvar my_printer "!my_printer" > *mygvars
  355.    bye
  356.  
  357. Whenever you type byebye, the setvar command is written to Mygvars and you are
  358. then logged off.  The default close disposition of an I/O re-direction file is
  359. TEMP, which is why you have to specify a file equation.  Because you are never
  360. certain  that  this file exists beforehand, doing a Purge ensures that it does
  361. not.
  362.  
  363. Program Control - If/Else and While
  364.  
  365. One of the reasons I like programming the CI is  its  lack  of  goto's.   This
  366. much-debated programming style has sparked many a heated discussion.  Consider
  367. the following:
  368.  
  369.    echo Powers of 2...
  370.    if "!hpjobname" = "KARNAK" then
  371.        setvar loop_count 1
  372.        setvar temp 1
  373.        while !loop_count < 10 do
  374.           setvar temp  !temp * 2
  375.           echo 2^!loop_count = !temp
  376.           setvar loop_count !loop_count + 1
  377.        endwhile
  378.    else
  379.       echo Sorry, you're not authorized to know.
  380.    endif
  381.  
  382. The above command file will display a powers of two table from one though nine
  383. for  the  user who is logged on as KARNAK.  It is good programming practice to
  384. indent code inside If and  While  blocks,  because  it  makes  debugging  much
  385. easier.  The CI doesn't care about leading spaces or blank lines, but you must
  386. use the Comment command to insert comments.
  387.  
  388. There are times when you wish to exit out of a loop in an  ungraceful  manner.
  389. To do this, use the Return command.  I often use this when I display help in a
  390. command file, and I don't want to clutter further code  with  a  big  if/endif
  391. block.
  392.  
  393.   parm hidden_away="?"
  394.   if "!hidden_away" = "?" then
  395.      echo This command file doesn't do much,
  396.      echo but it does it well.
  397.      return
  398.   endif
  399.   echo Local variable is !hidden_away.
  400.  
  401. Another way to terminate loops and nested command files is to use  the  escape
  402. command.   This command will blow you right back to the CI.  (Using the Return
  403. command only exits the current command file.)   You  can  optionally  set  the
  404. CIERROR jcw by adding a value to the end of the Escape command.
  405.  
  406.   escape 007
  407.  
  408. Simulating Arrays
  409.  
  410. It's true - arrays are not directly supported in the CI.  However, because  of
  411. some undocumented techniques (read: tricks), you can simulate arrays.
  412.  
  413. One  question  that may immediately pop into your head is "Why would I want to
  414. use arrays?"   Arrays are useful for table driven events,  such  as  returning
  415. days per month, sessions on ldevs, etc.
  416.  
  417. I won't keep you in suspense.  Here's the core method:
  418.  
  419. setvar !variable_name!variable_index   value
  420.  
  421. By using the  expression  evaluation  feature  of  the  CI,  you  can  have  a
  422. changeable variable name in the Setvar command.  CAVEAT USER:  This only works
  423. within command files!  If you try to do  this  interactively,  the  expression
  424. evaluation on the Setvar command is performed for the part of the command line
  425. after the variable name.  Within a command file, the entire line is  evaluated
  426. before being passed to the CI for re-evaluation.
  427.  
  428. In  much  the  same  way,  you  can  use  command files to change sequences of
  429. commands, i.e., to create self-modifying code.  For example,
  430.  
  431. weirdcmd
  432.    setvar variable_command "setvar apple ""orange"""
  433.    !variable_command
  434.  
  435. If  you  run  the command file, and then display the contents of the variable,
  436. you will see
  437.  
  438.    weirdcmd                {execute command file, above}
  439.    showvar apple
  440.    APPLE = orange
  441.  
  442. To simulate arrays, you must assign a variable per each element.  For example,
  443. you would assign months_12 for months(12).  This variable can be either string
  444. or numeric, but keep in mind that string variables can be up to 256 characters
  445. long.
  446.  
  447. Here are a few command files that allow you to manipulate arrays.
  448.  
  449. arraydef
  450.    parm array_name nbr_elements=0 initial_value=0
  451.    setvar array_index 0
  452.    while !array_index <= !nbr_elements do
  453.       setvar !array_name!array_index !initial_value
  454.       setvar array_index array_index + 1
  455.    endwhile
  456.  
  457. The  command  file  Arraydef allocates variables for each element of the array
  458. that you need.  The call sequence would be something like
  459.  
  460.    arraydef months_ 12
  461.  
  462. Just as you put an index in parentheses, I like to put underbars at the end of
  463. array names so that they are more readable.
  464.  
  465. You can use this command file to ensure that you have enough room  in  the  CI
  466. data  area to store your array.  Under MPE/iX release 5.0, space for variables
  467. is limited to roughly 20k bytes.  The space used can be calculated  by  adding
  468. the number of characters in each variable name, plus 4 bytes per integer item,
  469. or the length of each string.  For example, the integer variable "XYZZY" takes
  470. up  5 bytes for the name, and four more bytes for its integer value.  When you
  471. run out of space, you get the following error from the CI:
  472.  
  473. Symbol table full: addition failed.  To continue, delete
  474. some variables, or start a new session.  (CIERR 8122)
  475.  
  476. At the 1995 IPROF conference, I asked the MPE roundtable how much symbol space
  477. was  actually  available,  but  no  one  could give a definitive answer.  As a
  478. question to my question, the roundtable countered with "How many  people  have
  479. ever  run  out of symbol space?"  Only one person put up his hand in a room of
  480. 200 or so programmers - me.  Just wait until more people start using arrays.
  481.  
  482. The following command file allows  you  to  return  the  space  used  by  your
  483. pseudo-array when you are finished using it.
  484.  
  485. arraydel
  486.    parm array_name nbr_elements=0
  487.    setvar array_index 0
  488.    while !array_index < !nbr_elements do
  489.       deletevar !array_name!array_index
  490.       setvar array_index array_index + 1
  491.    endwhile
  492.  
  493. To demonstrate how arrays can be set (and values returned), the following  two
  494. command files, Arrayset and Arrayget, use the expression evaluation feature of
  495. the CI to convert the element number to the actual variable name.   Setvar  is
  496. called  to  set  either  the  value, or the name of the variable passed to the
  497. command file.
  498.  
  499. arrayset
  500.    parm array_name element_nbr=0
  501.    anyparm set_value
  502.    setvar !array_name!element_nbr !setvalue
  503.  
  504. arrayget
  505.    parm destination_var array_name element_nbr=0
  506.    anyparm get_value
  507.    setvar !destination_var !array_name!element_nbr
  508.  
  509. Here's a quick command file to show how you can use arrays in a  month  table.
  510. It  uses  the Input command to prompt the user for the number of days for each
  511. month.
  512.  
  513.    setvar months_nbr 0
  514.    while !months_nbr < 12 do
  515.       setvar months_nbr months_nbr + 1
  516.       input months_!months_nbr;                       &
  517.       prompt="Enter the number of days in month
  518.                                      !months_nbr:  "
  519.    endwhile
  520.    deletevar months_nbr
  521.  
  522. Calling All Functions!
  523.  
  524. The CI has a number of built-in functions  that  you  can  call  from  the  CI
  525. commands  If  and  Setvar.   The purpose of these functions is to provide some
  526. higher programming features at the CI level.  For  example,  when  you  accept
  527. user input, you may want to upshift the string to minimize your if logic.
  528.  
  529.    input yes_no,"Please enter yes or no:  "
  530.    if ups("!yes_no") = "YES" then
  531.  
  532. Since  people  generally like to reply with one-character answers, you can add
  533. one more function to your command file.
  534.  
  535.    input yes_no,"Please enter yes or no:  "
  536.    if lft(ups("!yes_no"),1) = "Y" then
  537.  
  538. In the above two examples, the ups() function upshifts  the  argument  string.
  539. lft()  returns  the  number  of  characters specified starting at the leftmost
  540. portion of the string argument.   If you  look  closely,  you'll  notice  that
  541. there are no quotes around the inside function call, that is you do not say
  542.  
  543.    if lft("ups("!yes_no")",1) = "Y" then
  544.  
  545. Because of CI expansion rules, you cannot use quotes when you embed a function
  546. within  a  function.   If  you think about it, the ups() function in the above
  547. statement would never get resolved.  If you play computer and expand the above
  548. statement, you get the following:
  549.  
  550.    IF lft("ups("yes")",1) = "Y" THEN
  551.  
  552. which, when executed, produces the error, A string operator was  expected  but
  553. none was found.  (CIERR 9755)
  554.  
  555. Here  are  a  couple  of useful functions.  You'll find the rest (as of MPE/iX
  556. 4.5) described at the end of this paper, or you can check Appendix B in Volume
  557. Two of the HP manual MPE/iX Commands.
  558.  
  559.    dwns( )    Shift string to lowercase
  560.    finfo( )   Returns file info, such as eof, etc.
  561.    rht( )     Returns rightmost count of characters
  562.    str( )     Extract a portion from the middle of a string
  563.  
  564. Popular Command Files
  565.  
  566. To  finish  off  this  paper, I've included some sample command files that not
  567. only illustrate previously discussed techniques, but also include  a  few  new
  568. tricks.
  569.  
  570. Obtaining  the  Remote  CIERROR  If you have multiple HP 3000's linked with NS
  571. services, then you will know exactly why you  need  this  functionality.   You
  572. cannot  get  the  remote  value  of the cierror JCW using traditional methods.
  573. More importantly, you cannot easily detect when a Remote command  fails.   For
  574. example, the following commands inside a job will not work as you expect:
  575.  
  576.   remote purge bigfile
  577.   if cierror <> 0 then
  578.      echo error!
  579.   endif
  580.  
  581. In fact, the HP Commands reference manual states:
  582.  
  583.   When  used to invoke commands on remote systems the COMMAND or HPCICOMMAND
  584.   intrinsics do not return a meaningful status code.  For  more  information
  585.   on  calling  intrinsics  refer  to  the MPE/iX Intrinsics Reference Manual
  586.   (32650-90028).
  587.  
  588. Clearly you must do a bit more work to get your desired results.  I've created
  589. two   command   files   that   you   will   need,  called  Remci.Cmd.Prod  and
  590. Echoci.Cmd.Prod.
  591.  
  592. remci.cmd.prod
  593.   remote echoci tempci
  594.   remote purge tempci > $null
  595.   remote save tempci
  596.   dscopy tempci:remmach;tempci;rep
  597.   continue
  598.   tempci
  599.  
  600. echoci.cmd.prod
  601.   parm tempfile
  602.   echo setjcw cierror !cierror > !tempfile
  603.  
  604. You will need to keep the Remci command file on the  local  machine,  and  the
  605. Echoci command file on the remote one.  The first thing that the Remci command
  606. file does is to remotely invoke the Echoci command  file.   This  inserts  the
  607. line  "setjcw  cierror [cierror value]" into the temporary file Tempci.  Remci
  608. then pulls the file over to the local machine via  dscopy,  and  executes  the
  609. file, setting the current cierror to the value of the remote cierror.
  610.  
  611. You  can  now insert this command file call into your first jobstream and make
  612. it work.
  613.  
  614.   remote purge bigfile
  615.   remci
  616.   if cierror <> 0 then
  617.      echo error purging remote bigfile!
  618.   endif
  619.  
  620.  
  621. Job Monitoring
  622.  
  623. In  a  perfect  world, jobs would never fail, and no one would never mistype a
  624. job number when aborting a job.  Since the world is  far  from  perfect,  it's
  625. nice to have something that helps monitor critical jobs.
  626.  
  627. checkjob
  628.    anyparm jobname
  629.    purge sojfile >$null
  630.    purge sojfile,temp >$null
  631.    purge grepout,temp >$null
  632.    purge grepout > $null
  633.    showjob job=@j;exec >sojfile
  634.    save sojfile
  635.    run grep.hpbin.sys;info="-ci '!jobname' SOJFILE" > grepout
  636.    save grepout
  637.    input nbr_found < grepout
  638.    if !nbr_found = 0 then
  639.       echo [esc]dB!jobname is not running.
  640.       setvar job_is_running FALSE
  641.    else
  642.       setvar job_is_running TRUE
  643.       echo !jobname is in exec status.
  644.    endif
  645.  
  646. A  gotcha about grep:  grep will only read permanent files, and it doesn't pay
  647. attention to file equations.  This powerful tool ported from UNIX is  used  to
  648. quickly  find  occurrences of strings within a file.  The -c option used above
  649. tells grep to return only the count of strings found, while  the  i  indicates
  650. that  the  search  shouldn't  care about upper or lower-case.  (Ignore case, I
  651. guess.)  In our command file, the search file is in the MPE  name  space,  and
  652. must appear in UPPER CASE.
  653.  
  654. Displaying Active Jobs
  655.  
  656. At Robelle, our shop occasionally has two to three hundred jobs in a scheduled
  657. state.  Typing Showjob produces pages of data  that  one  doesn't  necessarily
  658. wish  to  view.  The following command file called Sja will show only the jobs
  659. that are in the EXEC state, and some statistics about jobs in other states.
  660.  
  661. sja
  662.    echo JOBNUM  STATE IPRI JIN  JLIST    INTRODUCED
  663.    JOB NAME&[esc]&a65C[esc]&d!hpsysname
  664.    purge sojfile,temp >$null
  665.    showjob job=@j;exec >sojfile
  666.    setvar lastline finfo("sojfile","eof") - 4
  667.    print sojfile;start=4;end=!lastline
  668.    if !hpjoblimit < !hpjobcount then
  669.       echo [esc]&dA************ W A R N I N G ***********
  670.       echo [esc]&dBNumber of jobs executing exceeds
  671.                                                   JOBLIMIT
  672.       echo [esc]&dA**************************************
  673.    elseif (!hpjobcount + 1) < !hpjoblimit then
  674.       echo [esc]&dA******* W A R N I N G ****************
  675.       echo [esc]&dB  Joblimit will allow two jobs to run
  676.       echo [esc]&dB         at the same time
  677.       echo [esc]&dA**************************************
  678.    elseif !hpjoblimit = !hpjobcount then
  679.       echo [esc]&dH** WARNING:  The JLIMIT queue is full.
  680.    endif
  681.    echo &
  682.    [esc]&dJ  Sessions:  !hpsescount &
  683.    [esc]&dBJobs EXEC:  !hpjobcount (limit !hpjoblimit) &
  684.    [esc]&dB  WAIT:  !hpwaitjobs &
  685.    [esc]&dJ  SCHED: !hpschedjobs [esc]&d &
  686.    [esc]&dJ  SUSP: !hpsuspjobs
  687.  
  688. The above command file doesn't have any sneaky tricks, but  it  does  use  I/O
  689. redirection  and  a file function call to determine the number of lines in the
  690. output of the Showjob;exec command.  If you subtract four from  the  EOF,  you
  691. get the number of jobs executing.
  692.  
  693. Note  that  [esc] indicates the escape character, ascii 27.  That is, [esc]&dB
  694. is the escape sequence to turn on inverse video.
  695.  
  696. Creating a Job History
  697.  
  698. Have you ever streamed a  job,  cleared  your  screen,  then  wished  you  had
  699. remembered the job number that MPE assigned the stream?  The following command
  700. file, Jstream, takes care of this requirement  nicely.   It  streams  the  job
  701. passed  to  it,  and  maintains  a history of stream events.  It creates a new
  702. variable called jobhist_#, where # is  a  unique  job  number.   The  jobname,
  703. current  time  and  job  number  are  kept in this variable.  The command file
  704. Jobhist provides a simple history viewer.
  705.  
  706. jstream
  707.  
  708.   anyparm stream_jobname = ?
  709.   echo JSTREAM 1.2   Type JOBHIST to see your jobstream history.
  710.   if finfo("!stream_jobname",0) = FALSE then
  711.      echo Yowza!  I can't access !stream_jobname
  712.      escape 52
  713.      return
  714.   endif
  715.   setjcw cierror 0
  716.   continue
  717.   stream !stream_jobname > str52673
  718.   setvar temp_cierror !cierror
  719.   if temp_cierror <> 0 then
  720.      print str52673
  721.   endif
  722.   input stream_out < str52673
  723.   purge str52673,temp > $null
  724.   if lft(stream_out, 3) <> " J" then
  725.      escape !temp_cierror
  726.   endif
  727.   if bound(job_nbr_streamed) = FALSE then
  728.      setvar job_nbr_streamed 0
  729.   endif
  730.   setvar job_nbr_streamed !job_nbr_streamed + 1
  731.   if !job_nbr_streamed > 100 then
  732.      setvar job_nbr_streamed 1
  733.   endif
  734.   setvar jobhist_!job_nbr_streamed "!stream_out !hptimef  !stream_jobname"
  735.   echo Job !stream_out !hptimef  !stream_jobname
  736.  
  737. Jobhist
  738.   showvar jobhist  > jobhistt
  739.   print jobhistt
  740.  
  741.  
  742. Listing Spoolfiles
  743.  
  744. In order to see the output spoolfile from a  job,  you  must  first  know  its
  745. spoolfile  number.   This  task  is  really a number of steps which are easily
  746. replaced by the following command file.
  747.  
  748. ljob
  749.   parm jnum="@",listcommand="/ljq"
  750.   if "!hpjobtype" <> "S" then
  751.      echo I'm sorry, but LJOB can only be run
  752.                                         from a session.
  753.      return
  754.   endif
  755.   setvar ljob_jnum ups("!jnum")-"J"-"#"
  756.   if not numeric("!ljob_jnum")
  757.      echo
  758.      echo usage:  LJOB  job_number
  759.                                <,print command = '/LQJ'>
  760.      echo
  761.      echo         eg.  LJOB 1010,/lqj
  762.                                (use Qedit lqj command)
  763.      echo              LJOB 590,/lqj ]-50
  764.                                (list last 50 lines)
  765.      echo              LJOB 42
  766.      return
  767.   endif
  768.   setjcw cierror = 0
  769.   continue
  770.   spoolf o@;seleq=[jobnum=j!ljob_jnum and
  771.                      filedes="$STDLIST"] &
  772.           ;show >ljobtmp
  773.   if cierror <> 0 then
  774.      setvar ljobtmp_eof 0
  775.   endif
  776.   setvar ljobtmp_eof finfo("ljobtmp","eof")
  777.   if ljobtmp_eof < 4 then
  778.      echo Spoolfile for job !ljob_jnum does not exist.
  779.   else
  780.      print ljobtmp;start=4;end=4 >ljobtmp2
  781.      input spooline < ljobtmp2
  782.      setvar spoolid str("!spooline",2,7)
  783.      !listcommand !spoolid.out.hpspool
  784.   endif
  785.  
  786. The  default  print command in the Ljob command file is /lqj, which is a Qedit
  787. command to list the file without displaying line numbers, and to  pause  every
  788. 22  lines.   If you do not have Qedit, you can set the default list command to
  789. "print".
  790.  
  791.  
  792. CI Programming inside Qedit
  793.  
  794. You can use all MPE/iX CI commands in Qedit.  One of the  neatest  tricks  you
  795. can  try  is to get a line of data from a Qedit file into a variable.  You can
  796. do this with one of Qedit's undocumented features:  put a colon  in  front  of
  797. the  "/"  in a Qedit command, and then on the next line, use an Input command.
  798. For example,
  799.  
  800.   comment This command file puts the data from the current line
  801.   comment into the variable, myline
  802.   /listq *
  803.   /:/listq * > tempfile
  804.   input myline < tempfile
  805.  
  806. Qedit  versions  4.3 to 4.16 are inconsistent when you do a listq * > Tempfile
  807. first without doing a listq *.  Tempfile may or may  not  contain  two  lines,
  808. with  a  blank  first  line.   Doing  a  listq * guarantees that tempfile will
  809. contain only the current line of data, and no preceeding blank line.
  810.  
  811. This trick can really help you when you're adding custom  features  to  Qedit.
  812. Use  the following command file to find COBOL paragraph names without changing
  813. the current line pointer.  I use this feature when I can't  recall  the  exact
  814. name of the paragraph that I want to reference.
  815.  
  816. findpara
  817.    parm para_name left_window=7
  818.    :/ver zz > zztemp   {save old zz in temp file}
  819.    /zz */*             {save the current line number}
  820.    setvar g_para_name "!para_name"
  821.    setvar g_left_window "!left_window"
  822.    setvar right_window len("!g_para_name")
  823.                           + !g_left_window
  824.    /list "!para_name" (!g_left_window/!right_window)
  825.    :/list zz > $null   {return to line number w/o seeing
  826.                         the line number
  827.    /useq zztemp        {restore old zz}
  828.  
  829. You  can  invoke  Findpara  at  any time, but I usually use it while in Visual
  830. mode.  Paragraph names must start in column eight for  this  command  file  to
  831. work  properly.   You'll  notice  that  Findpara saves your old zz marker in a
  832. file, and restores the original after doing its work.  Qedit doesn't have this
  833. feature  -  hey!   We just extended Qedit's command set.  With a little bit of
  834. work, we could even have multiple bookmarks.  I'll leave this as  an  exercise
  835. for the reader.
  836.  
  837. Where to go from here...
  838.  
  839. No  doubt  you've  often  heard  that  the  only  way to learn something is to
  840. actually do it.  This thought applies directly to learning to program the CI -
  841. you must jump in and get your feet wet.  Start programming the CI today!
  842.  
  843. A  very  well  written  HP  manual,  the  MPE/iX  Command Reference Manual, is
  844. available on CD-ROM and on paper.   I  recommend  that  you  at  least  browse
  845. through  it.  Pay particular attention to Appendices A, B and C in Volume Two,
  846. which deal mostly with handling variables.
  847.  
  848. You can get instant help on the subject of variables.  As of  MPE/iX  5.0  the
  849. online  Help  command, help variables, will output pages of text.  (In pre-5.0
  850. environments, the online help in MPEX from VESOFT will  display  approximately
  851. the same thing.)
  852.  
  853. And  of course, if you really get stuck, you always have the Internet, and the
  854. list-server HP3000-L.  But that's a topic for another paper entirely.
  855.  
  856.    while awake = TRUE
  857.       program the_ci
  858.    endwhile
  859.  
  860. Glossary
  861.  
  862. Some  acronyms  require  no  explanation;  others  are  fairly  obscure to the
  863. uninitiated.  This short glossary explains some  of  the  terms  and  acronyms
  864. specific to this paper.
  865.  
  866. 3GL           Third  Generation  Programming  Language.   COBOL,  FORTRAN  and
  867.               Pascal are 3GL's.
  868. 4GL           A programming language for managers -  requires  less  knowledge
  869.               about the operating system and/or hardware internals.
  870. CI            Command  Interpreter.   This  is  the  program that is run right
  871.               after you log on.
  872. MPE/V         Mostly harmless.  Pre-cursor to the MPE/iX operating system, run
  873.               on overworked machines.
  874. HP3000-L      A list-server available via the Internet.  Lots of juicy tidbits
  875.               can be gleaned from this list.
  876. HPPATH        A string variable used by the CI as  a  reference  to  determine
  877.               where to look for programs and command files to execute.
  878. Interpreted CodePrograms that are compiled "on the fly".  Each statement in an
  879.               interpreted program is evaluated  every  time  it  is  executed.
  880.               Interpreted  code  tends  to  be  a  lot  slower to execute than
  881.               compiled code.
  882. IPROF         Interex Programmers Forum, held every spring at the HP Cupertino
  883.               labs.
  884. JCW           Job Control Word.  A integer variable limited to the CM space.
  885. Local Variable A  variable  that  can  be  referenced only within some "local"
  886.               area, such as a command file.
  887. Macro         A collection  of  commands  designed  to  perform  a  task  more
  888.               efficiently.
  889. Shell         A program, such as the CI, that is used as a working environment
  890.               for launching processes, editing code, and maintaining a command
  891.               interface.
  892. UDC           User Defined Command.  See page 2 for a description.
  893. WYGIWYG       What  You  Got  Is  What  You  Get.   Basically software without
  894.               support.
  895.  
  896. Addendum
  897.  
  898. Although this table appears in the Commands Reference manual, its  not  always
  899. convenient  to  browse  through  such  a  tome.   I  am  inserting it here for
  900. convenience.
  901.  
  902.                         Expression Evaluator Functions
  903. Symbol               Function               Example                    Result
  904. +(numeric)           addition               4 + 5                      9
  905. +(string)            concatenate            "abc" + "de"               abcde
  906. -(numeric)           subtraction            12 - 6                     6
  907. -(string)            deletion of first      "abc" - "b"                ac
  908.                      occurrence
  909. *                    multiplication         4 * 5                      20
  910. /                    integer division       79/ 10                     7
  911. ^                    exponentiation (9)     2^3                        8
  912. either " or '        string identifier      either "abc" or 'abc'      abc
  913. ()                   parentheses            (3 + 4) * 2                14
  914. <                    less than (1)          5 < 6                      TRUE
  915. <=                   less than or equal     "abc" <= "abc"             TRUE
  916.                      (1)
  917. >                    greater than (1)       "xyz" > "abc"              TRUE
  918. >=                   greater than or        "abc" >= "abc"             TRUE
  919.                      equal (1)
  920. <>                   not equal              5 <> 6                     TRUE
  921. =                    equal                  "xyz"= "xyz"               TRUE
  922. ABS(integer)         absolute value         abs(-4)                    4
  923. ALPHA(string)        check if a string is   alpha('abcd')              TRUE
  924.                      alphabetic             alpha('ab3d ef')           FALSE
  925. ALPHANUM(string)     check if a string is   alphanum('abCd')           TRUE
  926.                      only alphabetics and   alphanum('45abd')          TRUE
  927.                      digits                 alphanum('3d ef')          FALSE
  928. AND                  logical and            7=7 and 5=5                TRUE
  929. BAND                 bitwise and            7 band 13                  5
  930. BNOT                 bitwise not            bnot 5                     -6
  931. BOR                  bitwise or             5 bor 2                    7
  932. BOUND(varname)       variable               bound(HPPATH)              TRUE
  933.                      definition
  934.                      test (2)
  935. BXOR                 bitwise                7 bxor 5                   2
  936.                      exclusive or
  937. CHR(integer)         ASCII value            chr(65)                    A
  938.                      (integer) ===>
  939.                      character
  940. CSL                  circular shift         -2 csl 2                   -5
  941.                      left (3)
  942. CSR                  circular shift         -7 csr 1                   -4
  943.                      right (3)
  944. DWNS(string)         shift string           dwns('aBC&dE')          abc#de
  945.                      to lowercase
  946. FINFO(filename       file                   FINFO('x.pub',0)          TRUE
  947.        ,option)      information
  948. HEX(integer)         convert to             hex(329)                  $149
  949.                      hexadecimal
  950.                      string
  951. INPUT([prompt]        accept  user             input('Enter              Enter
  952. choice:  Y
  953.        [,wait])      input (10)              choice:',20)               Return
  954. "Y"
  955. LEN(string)          string length          len("abc")                3
  956. LFT(string,          left string            lft('abc',2)              ab
  957.     # chars)         extraction
  958. LSL                  logical shift          7 lsl 1                   14
  959.                      left
  960. LSR                  logical shift          -7 lsr 1          2,147,483,644
  961.                      right
  962. LTRIM(string         trim left end          'X'+ltrim(' abc')          Xabc
  963.       [,trimstr])    of string (11)         "X"+ltrim('...abc',        Xabc
  964.                                                   '.')
  965. MAX(num1[,num2...])  ind largest           max(5,4-3,70,0)             70
  966.                      of several
  967.                      integers
  968. MIN(num1[,num2...])  find smallest          min(5,4,-3,70,0)           -3
  969.                      of several
  970.                      integers
  971. MOD                  modulo (4)             25 mod 2                   1
  972. NOT                  logical not            not(2>1)                   FALSE
  973. NUMERIC (string)     check if a             numeric('12345')           TRUE
  974.                      string is all          numeric('$a234ef')         FALSE
  975.                      digits
  976. OCTAL(integer)       convert to             octal(329)                 %511
  977.                      octal string
  978. ODD(integer)         determine if           odd(233)                   TRUE
  979.                      integer is odd         odd(-2)                    FALSE
  980. OR                   logical or             5=5 or 2=3                 TRUE
  981. ORD(string)          ordinal (8)            ord('AbcD')                65
  982. POS(find str,source  find Nth               pos('ab','cgabd')          3
  983. str[,n])             occurrence of          pos('.','file.grp.acct',2) 9
  984.                      find str in            pos('.','file.grp.acct',-  9
  985.                      source str (-N         1)
  986.                      searches from
  987.                      right) (12)
  988. RHT(string, # chars) right string           rht("abc",2)               bc
  989.                      extraction
  990. RPT(string,count)    repeat a               rpt('aBc',3)          aBcaBcaBc
  991.                      string (-count         rpt('aBc',-3)         cBacBacBa
  992.                      reverses
  993.                      string)
  994. RTRIM(string         trim right end         rtrim('abc ')+'X'          abcX
  995.       [,trimstr])    of string (11)         rtrim('abc...','.')+"X "   abc X
  996. SETVAR(varname,expr) return result          setvar(myvar,2*3+5)  sets variable
  997.                      of expr and                                 myvar  to  11
  998. and
  999.                      set varname to                              returns 11
  1000.                      result (13)
  1001. STR(string,start     general string         str('abcde',2,3)     bcd
  1002. pos, # chars)        extraction
  1003. TYPEOF(expression)   type of                typeof(HPPATH)       2 (string)
  1004.                      variable or
  1005.                      expression (5)
  1006. UPS(string)          shift string           ups('aBc5d')              ABC5D
  1007.                      to uppercase
  1008.                      (7)
  1009. XOR                  logical                7=7 xor 5=5                TRUE
  1010.                      exclusive or
  1011.