![]() |
The NetRexx Tutorial
![]() |
One of the most important points for coding effective NetRexx programs, is the ability to dialogue with the operating system. Thus we want to be capable of executing OS commands, getting the output in a variable or in a array, starting other processes, and so on.
Sooner or later you will find yourself in the need to call a System Command from your NetRexx code, and have the output (if any) stored somewhere.
You should also note that you have ALWAYS an output from a System Command or Program. This is the Return Code rc from the Command itself.
Pictorially:
+---------+ +--------------------+ | O.S. |<----------| NetRexx | | (OS/2 | | Program | | UNIX |---------->|....................|---------> display | W95 | result | . | immediately | WNT) | | ....> save in a | | | | . VARIABLE | | | | ....> save in an | | | | ARRAY | +---------+ +--------------------+
As we have just stated, we will distinguish three cases:
We want also to make some decisions depending on the result of the command we just executed. If the command fails, i.e. exits with a $RETURN, not zero we want to be able to choose to continue, inform the user, or abort.
java.lang.Process java.lang.Runtime
This is probably the easiest instance: you want to execute an OS command (or a program). This means you will write:
(...) cmd = 'zip files.zip file1 file2' r = Runtime.GetRuntime() p = r.exec(cmd) (...)
It is ALWAYS a good practice to check the return code rc: a command or a program can fail for many reasons, and your program must be prepared for such eventualities. Note that if you do not check the rc, the program will happily continue with the following instructions. So we modify the above code as:
(...) cmd = 'zip files.zip file1 file2' r = Runtime.GetRuntime() p = r.exec(cmd) rc = p.exitValue() if rc <> 0 then do say 'Command "'cmd'" failed with rc:' rc'.' exit rc end (...)
This will allow us to check if the zip command in the above example didn't crash for a disk full problem, or for a missing input file.
Note that in the 2 above examples the output of the command is NOT displayed
WARNING: I feel necessary to warn you about a potential problems if you abuse of calls to System Commands.
You should NEVER use a call to System Commands if your call can be implemented in Java itself. So you should not (if you're a UNIX user) do:
-- -- NEVER DO THIS !!! -- ls = xexec('ls -l toto','VAR','ABORT') parse ls.out . . . . size .
This code is, infact, no portable (DOS and Windows) do not know about "ls".
NOTE: if you want to implement "ls" you do something like:
+----------------------------------------------------------------------+ | l = String[] |01 | f = File(".") |02 | l = f.list() |03 | loop i = 0 to l.length-1 |04 | say l[i] |05 | end |06 +----------------------------------------------------------------------+ lls.nrx | ![]() |
+----------------------------------------------------------------------+ | -- syex1.nrx |01 | -- SYstem EXec |02 | -- |03 | class syex1 public |04 | |05 | method main(args=String[]) public static |06 | |07 | arg = Rexx(args) |08 | parse arg cmd |09 | |10 | -- do the REAL job |11 | -- |12 | do |13 | rtim = Runtime.GetRuntime() |14 | proc = rtim.exec(cmd) |15 | dis = DataInputStream(proc.getInputStream()) |16 | |17 | loop forever |18 | line = dis.readline() |19 | if line = NULL then leave |20 | say line |21 | end |22 | rc = proc.waitFor() |23 | say 'Return code:' rc'.' |24 | catch err = IOEXception |25 | say 'ERROR:' err |26 | end |27 | exit 0 |28 +----------------------------------------------------------------------+ syex1.nrx | ![]() |
Some programs, like the following one, might require some "interactive" input.
+----------------------------------------------------------------------+ | n = 0 |01 | loop forever |02 | n = n+1 |03 | say 'Please enter something (quit to QUIT)' |04 | parse ask line |05 | if line = 'quit' then leave |06 | say n '>>>' line |07 | end |08 +----------------------------------------------------------------------+ interact.nrx | ![]() |
It would be nice if it was possible to make (when needed) the input "automatic". This small example shows how.
+----------------------------------------------------------------------+ | -- syex2.nrx |01 | -- SYstem EXec |02 | -- |03 | class syex2 public |04 | |05 | method main(args=String[]) public static |06 | |07 | -- this is the interactive command |08 | cmd = 'java interact' |09 | |10 | -- do the REAL job |11 | -- |12 | do |13 | rtim = Runtime.GetRuntime() |14 | proc = rtim.exec(cmd) |15 | dos = PrintStream(proc.getOutputStream()) |16 | dis = DataInputStream(proc.getInputStream()) |17 | dos.println('help') |18 | dos.println('quit') |19 | dos.close() |20 | |21 | loop forever |22 | line = dis.readline() |23 | if line = NULL then leave |24 | say line |25 | end |26 | rc = proc.waitFor() |27 | say 'Return code:' rc'.' |28 | catch err = IOEXception |29 | say 'ERROR:' err |30 | end |31 | exit 0 |32 +----------------------------------------------------------------------+ syex2.nrx | ![]() |
The "key" instruction is:
dos = PrintStream(proc.getOutputStream())
where we get an OUTPUT stream to the process proc. We now can simulate the keyboard input, which we do via:
dos.println('help') dos.println('quit')
so all is like if you were typing help and quit from your keyboard.
+----------------------------------------------------------------------+ | -- method......: xexec |33 | -- purpose.....: constructor |34 | -- |35 | method xexec(cmd=String,dest=Rexx,oner=Rexx) public |36 | dest = dest.upper() -- uppercase params |37 | oner = oner.upper() |38 | valid_dest = 'ARRAY SCREEN VAR NULL' |39 | valid_oner = 'WARNING ABORT IGNORE' |40 | |41 | -- setting the defaults |42 | -- |43 | if dest = '' then dest = default_dest |44 | if oner = '' then oner = default_oner |45 | |46 | -- check if the parms are OK |47 | -- |48 | if valid_dest.wordpos(dest) = 0 then |49 | do |50 | say 'Error: "'dest'" is not a valid destination.' |51 | exit 1 |52 | end |53 | if valid_oner.wordpos(oner) = 0 then |54 | do |55 | say 'Error: "'oner'" is not a valid ONERROR action.' |56 | exit 1 |57 | end |58 | |59 | -- do the real job |60 | -- |61 | do |62 | r = Runtime.GetRuntime() |63 | p = r.exec(cmd) |64 | cr = DataInputStream(BufferedInputStream(p.getInputStream())) |65 | |66 | -- Output handling |67 | -- |68 | lines = 0 |69 | out = '' |70 | j = 0 |71 | loop forever |72 | s = cr.Readline() |73 | if s = NULL then leave |74 | if dest.wordpos('SCREEN') |75 | then say s |76 | if dest.wordpos('VAR') |77 | then out = out s |78 | if dest.wordpos('ARRAY') |79 | then |80 | do |81 | j = j+1 |82 | line[j] = s |83 | end |84 | end |85 | lines = j |86 | line[0] = lines |87 | |88 | -- Return code handling |89 | -- |90 | rc = p.exitValue() |91 | if rc <> 0 then |92 | do |93 | select |94 | when oner = 'WARNING' then |95 | do |96 | say 'WARNING: rc=' rc 'from "'cmd'".' |97 | end |98 | when oner = 'ABORT' then |99 | do |00 | say 'WARNING: rc=' rc 'from "'cmd'".' |01 | say 'ABORTING.' |02 | exit 5 |03 | end |04 | otherwise NOP |05 | end |06 | end |07 | catch error = IOException |08 | say error |09 | end |10 | |11 | method xexec(cmd=Rexx,dest=Rexx) public |12 | this(cmd,dest,default_oner) |13 | |14 | method xexec(cmd=Rexx) public |15 | this(cmd,default_dest,default_oner) |16 | |17 +----------------------------------------------------------------------+ xsys.nrx(Method:xexec) | ![]() |
With the knowledge we developped in this chapter, we can now imagine to write a simple shell
+----------------------------------------------------------------------+ | -- package: xshell |01 | -- version: 1.000 beta |02 | -- date: 23 FEB 1997 |03 | -- author: P.A.Marchesini |04 | -- copyright: (c) P.A.MArchesini, 1997 |05 | -- latest vers.: http://wwwcn.cern.ch/news/netrexx |06 | -- |07 | -- This program is free software; you can redistribute it and/or mod|08 | -- it under the terms of the GNU General Public License as published|09 | -- the Free Software Foundation; either version 2 of the License,|10 | -- (at your option) any later version. |11 | -- |12 | -- This program is distributed in the hope that it will be useful, |13 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of |14 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |15 | -- GNU General Public License for more details. |16 | -- |17 | -- You should have received a copy of the GNU General Public License|18 | -- along with this program; if not, write to the Free Software|19 | -- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |20 | -- |21 | |22 | -- class xshell |23 | -- This class implements a "shell" environment, something like |24 | -- 'zsh' or 'bash' (with very less functions!) |25 | -- |26 | class xshell |27 | |28 | properties public static |29 | properties private static |30 | version = 'v0r000 beta' |31 | copyright = '(c) 1997 Pierantonio Marchesini, ETH Zurich' |32 | contact = 'Pierantonio.Marchesini@cern.ch' |33 | |34 | -- method......: shell |35 | -- purpose.....: constructor |36 | -- |37 | method xshell() public |38 | version = version -- make NetRexx happy |39 | copyright = copyright -- ditto |40 | contact = contact -- ditto |41 | |42 | -- method......: main |43 | -- purpose.....: just run typing "java shell" |44 | -- |45 | method main(args=String[]) public static |46 | args = args |47 | |48 | -- Initialization |49 | -- |50 | cmdno = 1 |51 | rc = 0 |52 | validlcmds = 'history' |53 | validecmds = 'ls pwd java' - |54 | 'ftp cp help dir' |55 | host = xsock.hostname() -- get my host,pls |56 | extracmd = '' |57 | his = history(100) |58 | |59 | loop forever |60 | say host '['his.counter()':'rc'] 'extracmd'\-' |61 | todo = ask |62 | if extracmd <> '' |63 | then todo = extracmd||todo |64 | |65 | -- check special cases |66 | -- |67 | if todo = '' then iterate |68 | if todo = 'exit' | todo = 'quit' then leave |69 | if todo.left(1) = '!' then |70 | do |71 | parse todo '!'rest |72 | select |73 | when rest = '!' then ptr=cmdno-1 |74 | otherwise ptr = rest |75 | end |76 | if ptr < 1 then ptr = 1 |77 | extracmd = his.retrieve(ptr) |78 | iterate |79 | end |80 | |81 | extracmd = '' |82 | cmdno = cmdno+1 |83 | his.save(todo) |84 | parse todo cmd arg |85 | arg = arg |86 | |87 | -- process local commands |88 | -- |89 | if validlcmds.wordpos(cmd) <> 0 then |90 | do |91 | select |92 | when cmd = 'history' then his.dump(10) |93 | otherwise say 'Sorry. "'cmd'" is not yet implemented.' |94 | end |95 | iterate |96 | end |97 | |98 | -- check for .class |99 | -- |00 | if xfile.fexist(cmd'.class') then |01 | do |02 | todo = 'java' todo |03 | cmd = 'java' |04 | end |05 | |06 | -- process external commands |07 | -- |08 | if validecmds.wordpos(cmd) = 0 then |09 | do |10 | say 'Invalid command "'cmd'".' |11 | iterate |12 | end |13 | c = xexec(todo,'SCREEN','IGNORE') |14 | rc = c.rc |15 | end |16 | exit 0 |17 +----------------------------------------------------------------------+ xshell.nrx | ![]() |
*** This section is:*** and will be available in next releases