A script can run any executable (ie, a program written in another language), including MS-DOS and Windows programs. For example, a script can run DOS's DISKCOPY.COM program, and even receive back an error code telling whether DISKCOPY performed its operation successfully (theoretically. Due to a bug in Windows's shell, 0 is always returned as the error code).

To run an executable, you must send a command string to an environment named 'CMD'. The first item in the command string should be the name of the program (executable) to run. Any other items in the command string would be the command line arguments passed to the program. (Typically, most programs allow the name of a data file to open).

For example, here we send the command string notepad.exe to run Windows notepad:

/* Make 'CMD' the current environment */
ADDRESS 'CMD'
/* Pass the command string */
'notepad.exe'
Notepad just happens to accept a command line argument which is the name of the text file to open. For example, here we send the command string notepad.exe myfile.txt to run Windows notepad and open the file named myfile.txt:
/* Make 'CMD' the current environment */
ADDRESS 'CMD'

/* Pass the command string */
'notepad.exe myfile.txt'

/* Restore the previous environment as current */
ADDRESS
If the operating system has trouble locating the program to run, you can specify the full path to it. For example, here we let Windows know that Notepad.exe is in the C:\Windows folder:
/* Make 'CMD' the current environment */
ADDRESS 'CMD'

/* Pass the command string */
'c:\windows\notepad.exe myfile.txt'

/* Restore the previous environment as current */
ADDRESS
For the remainder of these examples, we'll assume that 'CMD' is the current environment, and therefore eliminate the ADDRESS instruction from our examples.

You can also specify a full path on the data file's name.

Note: Reginald allows your script to run any "data file" simply by specifying its name. (ie, You don't need for the first item in your command string to be the name of the program that will open the data file). The program associated with that data file will automatically be run on that data file. For example, assume that the Windows Media Player is associated with MPEG files (ie, files whose name ends in .MPG). To play a video clip named myvideo.mpg located in your C:\VIDEOCLIPS folder, all you need to do is put the following line in your REXX script:

'C:\videoclips\myvideo.mpg'

As another example, assume that you have Internet Explorer associated with files that end in .htm. To bring up Internet Explorer and have it open the data file webpage.htm in the current directory, you would put the following line in your script:

'webpage.htm'
Note: If you wish to invoke an executable, and specify the filename to it using wildcards, then Reginald offers the ITERATEEXE() built-in function. For example, if you wish to play all of the files in a specific directory that end in .MPG extension, then you can use a single call to ITERATEEXE() and specify the filename as *.mpg.


Windows (shell) commands

You can also run Windows shell commands such as DIR, CD, ECHO, DISKCOPY, etc, by sending command strings to the environment named 'CMD'. The first item in the command string should be the name of the command to run. Any other items in the command string would be the command line arguments.

For example, here we pass a command string to the shell to delete all of the files in the current directory:

ADDRESS 'CMD'
'del *.*'

A shell command is different from a normal windows program in that a shell command does not create its own window to get any input and output. Instead, the shell command uses the command prompt window. Normally, the shell command takes its input as whatever arguments you specify in your command string. And for its output method, the shell command simply prints to the command prompt window.

But you can "redirect" the input and output of a shell command. For example, instead of having the shell command print its output text to the command prompt window, you can tell it to store that text (ie, PUSH each output line as a separate item) in the current Data stack. You can also tell the shell command to take its input arguments from the current Data stack (ie, PULL all of the items in the current stack, and use them as its arguments).

To tell a shell command to PUSH its output lines to the current stack, when you send your command string to the 'CMD' environment, specify an argument of '>LIFO'. If you instead prefer the shell command to QUEUE its output lines, then specify '>FIFO'. The '>LIFO' or '>FIFO' must be the last text in your command line.

For example, here we launch the 'DIR' shell command to get a directory listing of the directory named 'C:\mydir' and we tell the shell command to QUEUE each output line to the stack. Then after the shell command does its job, we SAY all of the items (ie, lines) that it pushed onto the stack.

ADDRESS 'CMD'

/* Do a DIR on 'C:\MyDir' and tell the shell
 * command to QUEUE the lines in the
 * current Data stack
 */
'dir C:\MyDir >FIFO'

/* Now SAY all of those lines */
DO WHILE QUEUED() \= 0
   PARSE PULL line
   SAY line
END
If you'd like the shell command to PULL its arguments from the current Data stack, then you need to preface your command string with 'LIFO>'. Before launching the shell command, you should PUSH the desired arguments onto the current Data stack. Remember that PUSH reverses the order of items, so PUSH your first argument last.

Here we push the name of the directory we want the DIR shell command to list, and then we tell DIR to get that name from the current Data stack.

ADDRESS 'CMD'

/* PUSH 'C:\MyDir' */
PUSH 'C:\MyDir'

/* Do a DIR on 'C:\MyDir' */
'LIFO> dir'
You can also combine input and output redirection:
ADDRESS 'CMD'

/* PUSH 'C:\MyDir' */
PUSH 'C:\MyDir'

/* Do a DIR on 'C:\MyDir' and tell the shell
 * command to QUEUE the lines in the
 * current Data stack
 */
'LIFO> dir >FIFO'

/* Now SAY all of those lines */
DO WHILE QUEUED() \= 0
   PARSE PULL line
   SAY line
END
Note: If you specify both input and output redirection, then Reginald's Script Launcher does not open up a console window. Otherwise, a console window will need to be opened.


Return code

REXX suspends your script (on that instruction that runs the program or windows command) while that program is running. When the program finishes running, it returns control back to REXX. The program/command also returns some numeric value that REXX automatically assigns to the variable named 'RC'. Most programs/commands return a 0 if they operate successfully. This is particularly true of the standard DOS commands such as DEL, COPY, etc). This value will typically be non-zero if there was an error. If the value is negative, then the FAILURE condition is raised. If the value is positive, then the ERROR condition is raised. So, you can often check the RC variable, or trap ERROR/FAILURE conditions, to see if the program did its job successfully.

'del *.*'
IF RC = 0 THEN SAY "It worked"
ELSE SAY "It didn't work"

Note: Due to a bug in the Windows shell, 'RC' is always set to 0, and FAILURE/ERROR conditions are never raised. So, it is impossible to verify whether a program/command performed successfully.


POPEN() built-in function

The above output redirection pushes each output line of the shell command onto the current stack. If you prefer, you can instead use the POPEN() built-in function to launch a shell command. POPEN() stores each output line of the command in a compound variable of your choice. For example, if you specify a stem variable name of MyVar., then the first output line of the command is stored in MyVar.1, the second output line of the command is stored in MyVar.2, etc. The variable named MyVar.0 will contain a count of how many lines were output by the command.

POPEN() returns the command's return code as described above.

For example, here we do a DIR using POPEN():

ADDRESS 'CMD'

/* Do a DIR on 'C:\MyDir' and tell the shell
 * command to store the lines in a variable
 * whose stem is MyVar
 */
code = POPEN('dir C:\MyDir', 'MyVar')

/* Now SAY all of those lines if successful */
IF code = 0 THEN DO
   DO i = 1 TO MyVar.0
      SAY MyVar.i
   END
END
ELSE SAY "It didn't work"
Note: Remember to quote the stem name passed to POPEN() unless you're specifying a variable whose value is the desired stem name. The passed stem name may end with a dot, but does not need to.

Note: Some interpreters do not support POPEN().


Reginald support

If using Reginald, then you have a number of built-in functions that can do things such as copy/delete/move files (ie, COPYFILE, DELETEFILE, MOVEFILE), get a directory listing (MATCHNAME), create/delete directories (DIR), set/query the current directory (PATH), etc.

Using one of these built-ins is better than issuing a shell command, or using POPEN, for the following reasons:

  1. These built-ins do not open a console window.
  2. They often execute faster than issuing a shell command.
  3. They are more portable across different versions of Windows. The built-ins should work the same in all versions of Windows, including future versions. On the other hand, the shell environment is a bit different between the Windows 95/98/ME line and the newer versions such as Windows 2000 or XP.
  4. They provide better error feedback. It is easier to tell when the operation has failed, and to obtain a meaningful error message. These functions usually either return such an error message when the operation fails, or return an error number for which you can obtain a message using UNIXERROR.
  5. They often offer additional features that are portable across different versions of Windows. For example, MATCHNAME lets you do things such as filter out only directory or file names, list only files that have certain attribute bits set, match only those directories/files that match a certain name pattern (such as all files whose names end in .HTM), etc.


Asyncronous operation

Microsoft Windows has a START command which causes the launched program to be start up as a separate process. In this case, control is returned to your script immediately upon launch of the other program. Your script doesn't have to wait for that other program to terminate before it can proceed doing other things. Your script continues executing while the other program is also executing. In this case, you won't be able to get an error code (returned in RC) from the other program when it terminates. Here we launch a program called BLORT.EXE:

'start blort.exe'