home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #18 / NN_1992_18.iso / spool / comp / lang / rexx / 738 < prev    next >
Encoding:
Text File  |  1992-08-19  |  17.7 KB  |  403 lines

  1. Comments: Gated by NETNEWS@AUVM.AMERICAN.EDU
  2. Path: sparky!uunet!charon.amdahl.com!pacbell.com!mips!darwin.sura.net!paladin.american.edu!auvm!DKNKURZ1.BITNET!RZOTTO
  3. X-Acknowledge-To: <RZOTTO@DKNKURZ1>
  4. Message-ID: <REXXLIST%92081920041037@DEARN>
  5. Newsgroups: comp.lang.rexx
  6. Date:         Wed, 19 Aug 1992 18:17:10 MEZ
  7. Sender:       REXX Programming discussion list <REXXLIST@UGA.BITNET>
  8. From:         Otto Stolz <RZOTTO@DKNKURZ1.BITNET>
  9. Subject:      Re: Capturing System Command Output (was: SH Backquote)
  10. In-Reply-To:  Message of Tue,
  11.               18 Aug 92 22:32:35 LCL from <anders@LISE8.LISE.UNIT.NO>
  12. Lines: 389
  13.  
  14. In article <19920818081215SEB1525@MVS.draper.com> SEB1525@MVS.draper.com
  15. (Steve Bacher) writes:
  16. > [...] they all have different conventions for
  17. > specifying how to route command output to the stack.
  18.  
  19. On Tue, 18 Aug 92 22:32:35 LCL Anders Christensen said:
  20. > [...] The three different _conceptual_ methods that I am aware of are
  21.  
  22. Below, I'll suggest a fourth method. But first, I'd like to make a few
  23. remarks on methods 1 through 3 of Anders' excellent article, including
  24. proposals for variants of these methods. I'll try to quote only small
  25. parts of the article; particularly, I'll not quote those parts I agree
  26. with and have nothing to add to.
  27.  
  28. If you do not want to scrutinise all of my considerations and examples,
  29. skip to a line containing the word Summary.
  30.  
  31. > (supposing that 'ls' is an opsys command that takes a file-spec as
  32. > parameter, which may contain wildcards, and returns all matching
  33. > filenames, one per line):
  34.  
  35. Apparently, Anders' examples assume that ls rather returns the filenames
  36. one per word; I'll leave the ls examples that way (including my own
  37. variants). In other examples, I'll use the actual conventions of the
  38. respective environments; note that the one-per-line convention (while
  39. permitting almost arbitrary items in the result) will render the examples
  40. a bit more verbose.
  41.  
  42. > 1) CALLED AS A FUNCTION
  43. >       files = 'ls'(directory)
  44. >       say 'There are' words(files) 'files'
  45. >       do i=1 while files \= ""
  46. >          parse var files file files
  47. >          say 'file number' i 'is' file
  48. >          end
  49. This would require that any command could be invoked as an external REXX
  50. function, inducing possible name-conflicts between REXX functions and
  51. system commands. Even if these conflicts are resolved by a visibility
  52. rule (aka search order), the REXX programmer will have to consider the
  53. system commands when choosing names for REXX functions (both internal and
  54. external ones).
  55.  
  56. A less cumbersome variant would be to have *one* built-in REXX function
  57. invoke *any* system command. This general routine would take a complete
  58. system command as its argument, and yield its standard output, as in
  59.         files = command("ls" directory)
  60.         say "There are" words(files) "files:"
  61.         do i = 1 while files ^= ""
  62.            parse var files   file files
  63.            say "File number" i "is" file
  64.         end i
  65.  
  66. An actually implemented example of this scheme: In REXX under CMS,
  67. CP commands may be invoked using the DIAG, or DIAGRC, function with
  68. 8 as its 1st, and the desired CP command as its 2nd (and optionally the
  69. buffer length for the expected result as its 3rd) argument, as in:
  70.         parse value diag(8, "QUERY FILES") ,
  71.               with . r .
  72.         if   datatype(r, "W")
  73.         then r = format(r)
  74.         else r = 0
  75.         say "There are" r "files in your Virtual Reader:"
  76.  
  77.         nl = '15'x                     /* new-line character */
  78.         parse value diag(8, "QUERY RDR ALL", (r+4)*81) ,
  79.               with (nl) files          /* remove header line */
  80.         say left("Id",4) left("Origin",8) left("Fname",8) left("Ftype",8)
  81.         do while files ^= ""
  82.            parse var files orig id . . . . . . . fn ft . (nl) files
  83.            say left(id,4) left(orig,8) left(fn,8) left(ft,8)
  84.         end /* files ^= "" */
  85.  
  86. An even more general variant would be a built-in function to invoke any
  87. command in any environment. This function would take the environment, and
  88. a suitable command, as its arguments, and yield the command's standard
  89. output, as in
  90.         files = invoke("command", "ls" directory)
  91.         say "There are" words(files) "files."
  92.         do i = 1 while files ^= ""
  93.            parse var files   file files
  94.            say "file number" i "is" file
  95.         end i
  96.  
  97.         nl = '15'x
  98.         parse value invoke("XEDIT", "QUERY RING") ,
  99.               with  . r . (nl) files
  100.         say "You are editing" r "files."
  101.         do i = 1 while files ^= ""
  102.            parse var files    file (nl) files
  103.            say "file number" i "is" subword(file,1,5) subword(file,7,1)
  104.         end i
  105.  
  106. You see, and appreciate, the uniformity (aka Principle of Least
  107. Astonishment)? (Cf. my general remark on the remaining discrepancies,
  108. at the end of this article).
  109.  
  110. > 2) EXTRA TERM IN COMMANDS [to] redirect the output [to] the stack:
  111. >       'ls' directory ' >LIFO'     /* or "|rxstack" */
  112. >       say 'There are' queued() 'files'
  113. >       do i=1 while queued()>0
  114. >          parse pull file
  115. >          say 'file number' i 'is' file
  116. >          end
  117.  
  118. This is actually implemented
  119. - in OS/2 as "| rxstack",
  120. - in Personal REXX as ">STK:",
  121. - in CMS as "( LIFO", "( FIFO", or "( STACK" -- though only for
  122.   particular commands,
  123. - and in CMS Rel 7 (and older CMS Releases with CMS-Pipes added) as
  124.   "PIPE cms" ... "| stack", as in
  125.        "PIPE cms ls" directory "| stack".
  126.  
  127. Excursus:
  128. As the stack is a global resource, it has to be used with care: every
  129. program should take pains to leave the stack in the state expected by
  130. other programs. Particularly, a program that uses the stack for its own,
  131. internal, purposes (as the program fragment above) should leave the
  132. stack unaltered. Hence, I always recommend the following "stack dis-
  133. cipline" for this sort of programs:
  134.         stack_level = queued()
  135.         "ls" directory " >LIFO"     /* or "|rxstack" */
  136.         say "There are" queued() - stack_level "files"
  137.         do i = 1 to queued() - stack_level
  138.            parse pull file
  139.            say "file number" i "is" file
  140.         end i
  141.  
  142. >    This is, of course, closely related to how it is done under CMS,
  143.  
  144. Minor correction:
  145. It used to be that way until (around Release 4) CMS commands learned to
  146. use the EXECCOMM interface, cf. method 4, infra.
  147.  
  148. >    where commands have options to flush the output to the stack, and
  149. >    where EXECIO can be used as a general purpose interface to the
  150. >    stack for operations that does not know about the stack.
  151.  
  152. Correction:
  153. EXECIO does this only for CP commands. There used to be no solution
  154. (short of re-reading the console output bound for the printer) for those
  155. CMS commands that neither deliver their results on the stack nor via
  156. EXECCOMM. Now, CMS-Pipes does the trick, uniformly, for all sorts of
  157. commands.
  158.  
  159. > 3) READING COMMANDS WITH LINEIN.  A command is started with a builtin
  160. >    function, and the output is read using the LINEIN() function.
  161. >       rcode = popen( "ls" directory,, "ls-output" )
  162. >       say 'There are' lines('ls-output') 'files'
  163. >       do i=1 while lines() \= 0
  164. >          say 'file number' i 'is' linein('ls-output')
  165. >          end
  166.  
  167. How will the interaction between the REXX program and its environment
  168. (or environments) be handled, in this scheme? Note that the particular
  169. strength of REXX lies in this very interaction: REXX programs issue
  170. commands depending on the results of preceding ones. Take the recent FTP
  171. thread as a vivid example: you would like to proceed only when the con-
  172. nection could be established properly, or you would like your REXX pro-
  173. gram to query the contents of some directory then decide what files to
  174. transfer. Hence, we need a particularly safe, convenient, and efficient
  175. scheme for this sort of interaction.
  176.  
  177. To enhance the preceding example, let fs. be a family of REXX variables
  178. containing a sequence of file-specs (possibly containing wildcards), and
  179. let us try to list all files matching those specs.
  180.  
  181. Will we have to open and close the stream for every command to be issued?
  182. This would naturally provide for multiple-line responses, but it would
  183. probably be horribly inefficient. Example of the possible syntax:
  184.         files = ""
  185.         do i = 1 to fs.0
  186.            call popen "ls" fs.i, "ls-output"
  187.            files = files linein("ls-output")
  188.            call pclose "ls-output"
  189.         end i
  190.         say "There are" words(files) "files."
  191.         do i = 1 to words(files)
  192.            say "file number" i "is" word(files,i)
  193.         end
  194.  
  195. Another approach would be to open, and close, the stream once and for
  196. all. Then we would need another built-in function, say pcommand, to issue
  197. subsequent commands, as in:
  198.         call popen "ls" fs.1, "ls-output"
  199.         files = linein("ls-output")
  200.         do i = 2 to fs.0
  201.            call pcommand "ls" fs.i, "ls-output"
  202.            files = files linein("ls-output")
  203.         end i
  204.         call pclose "ls-output"
  205.         say "There are" words(files) "files."
  206.         do i = 1 to words(files)
  207.            say "file number" i "is" word(files,i)
  208.         end
  209.  
  210. But then, why invent so many new functions, at all? Let us issue the
  211. commands in the conventional, REXXish, way via the address statement!
  212. All we need for method 3 is a connection between the standard-output
  213. (generated by some environment) and some input stream. Most naturally,
  214. the input stream and the environment could be associated via a common
  215. name. However, we cannot unconditionally bind the command output to
  216. a REXX input stream, as this would invalidate existing REXX programs,
  217. viz. those invoking a command in order to have its output presented
  218. directly to the user, as in
  219.            address command "ls" directory
  220.  
  221. Hence, I propose to bind the command output explicitely to the cor-
  222. responding REXX input stream. We could achieve this via an enhanced
  223. address statement, e.g. a new keyword, separated by a comma. With this
  224. proposal implemented, two of the above examples would burn down to:
  225.         files = ""
  226.         do i = 1 to fs.0
  227.            address command "ls" fs.i, Linein /* explicit address stmt. */
  228.            files = files linein("command")
  229.         end i
  230.         say "There are" words(files) "files."
  231.         do i = 1 to words(files)
  232.            say "file number" i "is" word(files,i)
  233.         end
  234.  
  235.         address "XEDIT", Linenin
  236.         "QUERY RING"  /* cf. supra, method 1 */    /* implicit address */
  237.         parse value linein("XEDIT") ,
  238.               with  . r .
  239.         say "You are editing" r "files."
  240.         do i = 1 while files ^= ""
  241.            file = linein("XEDIT")
  242.            say "file number" i "is" subword(file,1,5) subword(file,7,1)
  243.         end i
  244.  
  245. The basic idea is an explicit binding of the command output (from any
  246. environment) to a linein-stream; the particular syntax is negotiable :-)
  247.  
  248. > No doubt, there are several conceptual additional methods ... anyone?
  249.  
  250. 4) Exploit the Variable Pool Interface:
  251.    A command invoked via the usual (explicit or implicit) address in-
  252.    struction will deliver its results to a REXX stem or variable.
  253.  
  254.    Section 5 of Cowlishaw's "The REXX Language" (TRL) states that a
  255.    command to an external environment "may have side-effects". One con-
  256.    ceivable side effect (though not explicitely deemed so in TRL) would
  257.    be the setting of a REXX variable, such as RC which is set in any
  258.    case.
  259.  
  260.    The official IBM definitions of REXX,
  261.    - SC26-4358 "SAA CPI Procedures Language Reference", and
  262.    - SC26-5549 "SAA CPI Procedures Language Level 2 Reference",
  263.    carry this concept one step further: under the heading of
  264.    "Variable Pool Interface", they state that every SAA conforming REXX
  265.    implementation "provides an interface whereby called commands can
  266.    easily access and manipulate the current generation of Procedure
  267.    Language [i.e. REXX] variables".
  268.  
  269.    In CMS, this interface is called EXECCOMM (cf. SC24-5239-2, p 159).
  270.    In TSO/E, it is called IRXEXCOM (cf. SC26-1883-0, p. 240). In OS/2,
  271.    it is called Variable Pool Interface (cf. OS/2 Procedures Language
  272.    Reference, Chapter on "Applications Programming Interface" -- I have
  273.    neither the IBM form number nor the exact page available; it should
  274.    be somewhere around page 180). In Personal REXX (non-IBM product), it
  275.    is called Shared Variable Interface (cf. "Personal REXX User's Guide"
  276.    Ver 1.60, p. 145 and p. 162). I don't know about possible Variable
  277.    Pool Interfaces in other REXX implementations. Any volunteers to
  278.    tell us?
  279.  
  280.    In CMS, this interface has been used in several ways that may serve
  281.    as general examples.
  282.  
  283. 4.1) Implicit REXX variable:
  284.    Back in CMS Rel 3, the XEDIT learned to set REXX variables.
  285.    One of the above examples would rather be programmed as:
  286.         address XEDIT "COMMAND EXTRACT /RING/"
  287.         say "You are editing" ring.1 "files."
  288.         do i = 2 to ring.0
  289.            say "file number" i-1                                    ,
  290.                "is"          subword(ring.i,1,5) subword(ring.i,7,1)
  291.         end i
  292.  
  293.    The result of the EXTRACT command is placed in the (implicitly
  294.    chosen) variables ring.0, ring.1, ring.2, etc.
  295.  
  296.    In CMS Rel 5, particular commands (PARSECMD, XMITMSG), designed for
  297.    close interaction with REXX code, were added. They deliver their
  298.    results in the MESSAGE., CODE., and TOKEN., stems. I gave several
  299.    examples in the proceedings of the SHARE Europe Spring Meeting
  300.    1990, Volume 1, pages 197 through 224, which I won't repeat here.
  301.    Whoever is interested in those particular commands may ask privately
  302.    for a copy of my presentation.
  303.  
  304. 4.2) Arbitrary, explicitely specified, variable:
  305.    In CMS Rel 4, the EXECIO command was enhanced to deliver its results
  306.    in an arbitrary REXX stem, or variable. CMS-Pipes (CMS 7, or add-on
  307.    to lower releases) can deliver results from any command in an arbi-
  308.    tray REXX stem or variable. Example:
  309.         address command "PIPE cms ls" directory | var files"
  310.         say "There are" words(files) "files:"
  311.         do i = 1 while files ^= ""
  312.            parse var files   file files
  313.            say "File number" i "is" file
  314.         end i
  315.  
  316. Summary of conceivable methods to access command output:
  317.  
  318. 1. Command output is yielded as a function result.
  319. 1.1 System command verb is used as REXX function name;
  320.     command argument string is given as REXX function argument.
  321. 1.2 Built-in REXX function invokes system command;
  322.     entire command is given as REXX function argument.
  323. 1.3 Built-in function invokes command in any environment;
  324.     environment and entire command, are given as REXX function arguments.
  325.  
  326. 2. Command output is delivered on program stack.
  327. 2.1 Command syntax of environment allows
  328.     to specify stack as a target for command output.
  329.  
  330. 3. Command output is delivered to a REXX input stream
  331.    (to be read via LINEIN).
  332. 3.1 Built-in function issues system command,
  333.     and binds its output to a particular REXX input stream;
  334.     entire command, and stream-id, are given as REXX function arguments.
  335. 3.2 Built-in function issues 1st system command
  336.     and binds system command's output to particular REXX input stream;
  337.     entire command, and stream-id, are given as REXX function arguments.
  338.     Another built-in function to issue subsequent commands,
  339.     while preserving the output binding;
  340.     entire command, is given as REXX function arguments.
  341. 3.3 Explicit binding of REXX command environment
  342.     to REXX input stream.
  343.  
  344. 4. Command output is delivered to REXX variables.
  345. 4.1 Implicit REXX stem or variable, depending on command issued.
  346. 4.2 Command syntax allows to specify target stem or variable.
  347.  
  348. I'd prefer methods 4.2, 1.3, 2.1 and 3.3, in this order.
  349.  
  350. In article <19920818081215SEB1525@MVS.draper.com> SEB1525@MVS.draper.com
  351. (Steve Bacher) writes:
  352. > The best approach available to date, hinted at by one of the posts, is
  353. > to write a general-purpose exec that will interrogate the type of REXX
  354. > implementation environment and do the appropriate thing.
  355.  
  356. On Tue, 18 Aug 92 22:32:35 LCL Anders Christensen said:
  357. > This is not an acceptable permanent solution, since any program is
  358. > outdated the very moment a new rexx interpreter is introduced.
  359.  
  360. Not quite so: Only Steve's general-purpose exec (i.e. external REXX
  361. function) would be outdated; when it is amended to cover the new imple-
  362. mentation, all programs would be valid again. You may view that general-
  363. purpose exec as a private implementation of either method 1.2 or method
  364. 1.3, above.
  365.  
  366. > A better solution might be to label the _methods_ rather than the
  367. > _interpreters_, then it would be possible to check whether an
  368. > interpreter supports e.g. the popen-based method.
  369.  
  370. This would require an amendment to the REXX standard and to current REXX
  371. implementations, while Steve's proposal can be immediately implemented
  372. by any REXX programmer, exploiting the existing Parse Source and Parse
  373. Version statements.
  374.  
  375. Remaining discrepancies:
  376. Even if one or several of the methods outlined above would become
  377. standard, REXX programs could not be more portable than their environ-
  378. ments would allow them to be! (viz. virtually not portable :-( )
  379.  
  380. As a simple example, consider the syntax of file identifiers in various
  381. systems: In CMS, it is 3 words (but only for a disk file), in MS-DOS
  382. it is one word, possibly containing a colon and several backslashes, in
  383. Unix you have slashes instead, etc etc. Some of these discrepancies are
  384. more than mere syntactic idiosyncracies: they reflect different file
  385. system concepts, requiring different approaches and algorithms to deal
  386. with. How can a REXX program be portable between such diverging environ-
  387. ments?
  388.  
  389. Moreover, the desired results from the command may be separated in
  390. various ways (by blank space, by line-breaks, by syntactic marks), or
  391. imbedded in irrelevant information.
  392.  
  393. The only way for the REXX programmer to strive for portability is to
  394. invent his or her own, system independend, set of primitives, implement
  395. them seperately for every system the programs are intended for, and use
  396. them in the rest of the programs in a system independend way.
  397.  
  398. Thank you for reading this far (385 lines).
  399.  
  400. Best wishes,
  401.                     Otto Stolz <RZOTTO@DKNKURZ1.Bitnet>
  402.                                <RZOTTO@nyx.uni-konstanz.de>
  403.