REXX Tips & Tricks:General hints for OS/2 & DOS commands
This section contains some useful hints for using OS/2 & DOS commands (with REXX programs and without REXX programs).
Contents
- 1 CTRL-Break & OS/2 commands
- 2 Using meta chars
- 3 STDIN, STDOUT, STDERR, ...
- 4 Hints for some OS/2 commands
- 4.1 The ARCRECOV command
- 4.2 The BACKUP command
- 4.3 The BOOT command
- 4.4 The CACHE command
- 4.5 The CALL command
- 4.6 The CHKDSK command
- 4.7 The CMD command
- 4.8 The COPY command
- 4.9 The DETACH command
- 4.10 The DIR command
- 4.11 The FDISK command
- 4.12 The EXTPROC command
- 4.13 The FORMAT command
- 4.14 The HELP command
- 4.15 The SET command
- 4.16 The SHUTDOWN command / The SETBOOT command
- 4.17 The SYSINSTX command
- 4.18 The XCOPY command
- 5 Hints for some DOS commands
- 6 Reserved directory & file names
CTRL-Break & OS/2 commands
There's a bug in CMD.EXE in the handling of CTRL-BREAK while executing a system command from within a REXX program: Instead of passing this signal to the REXX program, the CMD.EXE terminates the REXX program. This is also true for .EXE files called from a REXX program if they pass the CTRL-Break to the CMD.EXE. (Source: page How do I ... on the home page of Quercus Systems (see Internet - Web Pages))
To avoid this error, call a new copy of the CMD.EXE to execute system commands. For example:
"cmd /c" "dir \ | rxqueue" /* the variable RC contains the return code */ /* of the dir command */
(see also #The CMD command and #The functions LINEIN() and PULL())
Using meta chars
To use one of the OS/2 meta chars (e.g. |, <, >, ...) as normal, literal chars in OS/2 commands you can precede the meta char with a caret ^. This char, known as an "escape character", will keep the meta char from being interpreted by the shell.
Thus, whereas the meta char ">" normally will redirect output from a program or other source, the two characters "^>" will be seen by the system as ">" with no special meaning. This is one way in which you could execute the following command without producing an error:
echo The objectID of the Connection folder is ^<WP_CONNECTIONSFOLDER^>.
To use a literal caret character you must use ^^.
The other method to use these chars as literal chars is to surround the whole string with double quotes ".
To prevent the CMD.EXE from interpreting the percent char % you can also use two of them: %%. That's also necessary if you want to use the percent char in a string enclosed in double quotes.
To use filenames beginning with a dash (-) without having the system interpret the filenames as command parameters, use either the absolute path or a relative path in your command.
Example:
attrib -r .\-test.log attrib -r c:\temp\-test.log
- Note
- Users of alternate command shells such as 4OS2 should check to see what char their command interpreter is using as the escape char (CMD.EXE uses "^") before using the caret symbol. Some command interpreters allow the user to change the character used. This is usually controlled by a setting in an INI file or environment variable.
STDIN, STDOUT, STDERR, ...
STDOUT / STDERR
Some OS/2 commands (for example DEL) write their output to STDERR if STDOUT is redirected to a file, pipe or device.
Redirect STDOUT and STDERR
If you want to redirect STDOUT and STDERR of an OS/2 command, you should always redirect STDERR first. If, instead, the reverse of this is used, some programs (including CMD.EXE) sometimes write their error messages to the display regardless of the redirection of STDERR.
To redirect STDOUT and STDERR into the same FILE use the following construct:
"attrib a:test.cmd 2>>errFile 1>>&2"
To redirect STDOUT and STDERR into the same PIPE use the following construct:
"attrib a:test.cmd 2>&1 | pipeit.cmd"
Please note that other command line interpreter, such as 4OS2, for instance, may use another syntax for the redirection of STDOUT and/or STDERR.
- Note
- See also #STDOUT / STDERR, #How to check if STDIN, STDOUT or STDERR are redirected to a file see #General input line enhancer for an example use of STDERR and STDOUT.
Using Pipes
The number of PIPEs (|) in one OS/2 command is limited to n, where n depends on the length of the current value of the PATH variable. To get around this limitation, group together parts of your command in parentheses.
Example: Instead of
a | b | c | d | e | f
use
(a | b | c) | (d | e | f)
(where a, b, c, d, e and f are OS/2 commands or programs). The parts in parentheses are executed by separate CMD processes and no CMD process must create more than 2 PIPEs.
- Source
- c't 1/1996
(see also Pipes & Errorlevel)
Using "Named Pipes"
It should be possible to use Named Pipes as input or output device for OS/2 commands, for example:
/* create a Named pipe called "\PIPE\MYPIPE" */ /* with on of the functions from */ /* RxNPipe - Named Pipes for REXX */ /* RexxIPC - interprocess communication for REXX */ /* RXU - powerful DLL for REXX */ /* now call for example the cmd to use that pipe for STDIN */ "start /c cmd <\PIPE\MYPIPE" /* now you could write the commands for the cmd to execute */ /* to the pipe \PIPE\MYPIPE */
But until now I did not manage to implement a working solution using Named Pipes for this purpose.
How to check if STDIN, STDOUT or STDERR are redirected to a file
To check, if STDIN, STDOUT or STDERR are redirected to a file use the following code:
/* This code works in Object-Oriented REXX only! */ STDIN_type = stream( 'STDIN', 'c', 'QUERY STREAMTYPE' ) if STDIN_type = "PERSISTENT" then say "STDIN is redirected to a file." else say "STDIN is the screen or a pipe or NUL." STDOUT_type = stream( 'STDOUT', 'c', 'QUERY STREAMTYPE' ) if stdout_type = "PERSISTENT" then say "STDOUT is redirected to a file." else say "STDOUT is the screen or a pipe or NUL." STDERR_type = stream( 'STDERR', 'c', 'QUERY STREAMTYPE' ) if stderr_type = "PERSISTENT" then say "STDERR is redirected to a file NUL." else say "STDERR is the screen or a pipe."
Pipes & Errorlevel
If you're using pipes in REXX programs, remember that the special REXX variable RC always contains the return code of the last program in the command containing the pipes.
Example:
/* */
address cmd 'dir | sort | find "<DIR>" '
/* The next statement will always print the */ /* return code of the find command as the */ /* value of RC */ say 'RC=' || rc
To read the return codes of the programs in the middle of the pipe as well, you can use a REXX wrapper.
Example:
/* ------------------------------------------------------------------ */
/* PushRC - simple wrapper program to save the return code of an */
/* OS/2 command or program in the current REXX queue */
/* */
/* Usage: pushRC program parameter */
/* */
/* History */
/* 01.09.1996 /bs v1.00 */
/* - initial release */
/* */
/* */
/* */
/* */
parse arg progName progParameter
"cmd /c" progName progParameter
/* progname (or something similar) is necessary */ /* because OS/2 is a multi-tasking */ /* operating system */ push progName "RC="rc
Usage example:
/* */ address cmd "pushrc dir | pushrc sort | pushrc find ""<DIR>"" " do while queued() <> 0 say lineIn( "QUEUE:" ) end /* do while queued() <> 0 */
(see also #Using PIPEs)
Hints for some OS/2 commands
This section contains hints for some OS/2 commands. Some of these hints are not well known, but they may help you get more out of the commands or help users to employ the commands more productively.
Note that the information about internal commands is only valid for the CMD.EXE. It might not be correct for other command line interpreters, such as 4OS2.
The ARCRECOV command
This command is used to restore the desktop from a previously created desktop archive. The syntax is:
ARCRECOV x
where x can be 1, 2, 3 or X.
The reason for these numbers is that WARP's desktop archiving facility stores up to a maximum of 3 archives, plus its original files (X).
The current version of ARCRECOV does not display any usage instructions.
Source: The OS/2 WARP Survival Guide
The BACKUP command
The BACKUP command in WARP 4 supports the undocumented switch /O. When this switch is used, BACKUP will process open files.
(WARP 4 only!)
Source: The OS/2 WARP Survival Guide, APAR JR09859.
The BOOT command
The BOOT command supports the undocumented switch /N. Using this switch, the BOOT command prepares the hard disk for the requested OS but it will not reboot the system.
Source: The OS/2 WARP Survival Guide
The CACHE command
Use the undocumented switch
/WRITECACHE:size_in_bytes
of the CACHE command to specify the maximum size of writes to be cached. The default value for this parameter is 65,536; the maximum is also 65,536.
Source: The OS/2 WARP Survival Guide
Use the undocumented switch /Dstg:on to get some more information and switches for CACHE.EXE. Use CACHE /Dstg:on /? to get a syntax help for the undocumented switches.
(WARP 4 only! but the switches /DirtyMax:n and /Writecache:n also work in WARP)
Source: Team OS/2 WEB pages
The CALL command
Use the OS/2 command CALL with the undocumented switch /Q to suppress all messages from the called CMD. (Do not confuse the OS/2 command CALL with the REXX statement CALL!)
Source: The OS/2 WARP Survival Guide
The CHKDSK command
There must be at least one blank between the drive specifier and the slash indicating further options if using CHKDSK for an HPFS formatted drive.
Example:
CHKDSK C:/F
will work for FAT formatted drives, but not for HPFS formatted drives.
CHKDSK C: /F
will work for both, FAT formatted and HPFS formatted drives.
The CMD command
The /S parameter does not disable the signal handler for CMD.EXE as documented.
Source: APAR PJ18883
The value for the parameters /P and /K must be enclosed in two leading and two trailing double quotes if the name of the program to execute contains blanks or special chars.
Example
cmd /c ""MY TEST.CMD""
Source: Brian Havard (see EMail Addresses)
The COPY command
The switches /Y and /-Y for the COPY command are only mentioned in the Online Help but not yet implemented.
(WARP 4 only!)
Source: APAR JR09625
Copying open or locked files
To copy locked DLLs or EXEs you can unlock them with the unlock program from the LXlite package.
To copy open files you can use the program ROBOSVUT.COM from the RoboSave package.
And last, if you don't have one of the tools mentioned above handy: Use the TYPE command to copy an open text file:
type openFile >targetFile
(see Detach a program for an usage example; see also The BACKUP command)
Caution: Use this method only for ASCII files because TYPE interprets some ASCII codes!
Copying empty files
You cannot copy an empty file (zero byte length) with the COPY command. To copy an empty file you may use the TYPE command:
type emptyFile >targetFile
Note that the XCOPY command can copy empty files. The COPY command of 4OS2 can also copy empty files. And, of course, you can also write a REXX program to copy empty files.
The DETACH command
The DETACH command writes the message with the process ID of the detached program to STDERR.
To catch this ID, you can redirect STDERR of the DETACH command into a file (see Redirect STDOUT and STDERR). Than you can use any program that can read open files to process this file.
Unfortunately REXX programs cannot read open files. Therefore you have to use a little trick to get the PID in a REXX program (see Detach a program)
Note: The STDERR of the DETACH command is used by the DETACH command and by the detached process. Therefore, if you redirect STDERR of DETACH into a file, this file is open until the detached process ends. Note also that you cannot pipe the output of the DETACH command to another program.
All programs started from a detached session use the standard input, output and error handles of the detached session. You cannot use the START command to start another session from a detached session. You can start some but not all PM programs from a detached session. For example PMREXX.EXE works but VIEW.EXE doesn't. To start a usable, interactive CMD session from a detached session you might use the command:
"pmrexx rexxtry cmd"
Or, a more general solution, use the WPS interface: SysSetObjectData. Example:
rc = SysSetObjectData( '<WP_OS2WIN>', 'OPEN=DEFAULT;' )
The DIR command
Use the undocumented switch /V of the DIR command to produce formatted output.
(WARP 4 only!)
Source: Team OS/2 WEB pages
The FDISK command
Use the undocumented switch /newmbr to force FDISK to write a new master boot record to the harddisk. This is equal to the switch /mbr of the DOS FDISK command. The switch is documented in the Online Reference of WARP 4.
If FDISK refuses to do something you want it to do (e.g. create or delete a partition), use FDISK in non-interactive mode with parameters. In most cases this helps.
Please be aware that when FDISK is run in non-interactive mode the position (i.e., order) of parameters given on the command line is significant.
Note: Do not rely on the return code of FDISK.
The EXTPROC command
You can use the EXTPROC command to instruct the CMD.EXE to automatically call your REXX program for your data files.
To provide a datafile with this capability, you must give it the extension .CMD and use EXTPROC as the first line in the datafile. The EXTPROC statement in the datafile will call the REXX program you specify following EXTPROC. (see examples below or download RxLBox for a working example). In addition, you will need to set up the specified REXX program to ignore the line of the datafile that starts with EXTPROC.
Example: Let's say we have a datafile called MYDATA1.CMD. The contents of the file may look like:
EXTPROC myprog1 datarecord 1 datarecord 2 datarecord 3
The REXX program MYPROG1.CMD could be launched automatically from MYDATA1. It might look like:
Example:
/* MYPROG1.CMD */ /* - sample REXX program for processing datafiles using EXTPROC */ /* */ /* This program assumes that the only parameter of the EXTPROC */ /* statement is the name of the program to call. The name of the */ /* datafile is automatically added by the CMD.EXE. */ /* You can use the fully qualified name of your CMD in the EXTPROC */ /* statement. Or you can use only the name of your CMD (without */ /* the path) if it's available via the environment variable PATH. */ /* Example: */ /* */ /* EXTPROC MYPROG1.CMD */ /* */ /* or */ /* */ /* EXTPROC C:\test\MYPROG1.CMD */ /* */ /* This programs works only if the datafile is in the current */ /* directory when it is run (see below) */ /* */ parse arg dataFile say 'The name of the datafile is:' say ' "' || datafile || '"' /* now read the data file */ fileStem.0 = 0 i = 0 do while lines( dataFile ) <> 0 /* ignore the EXTPROC line */ curLine = lineIn( dataFile ) if translate( word( curLine, 1 ) ) = 'EXTPROC' & i = 0 then iterate i = i+1 fileStem.i = curLine end /* do while lines( dataFile ) <> 0 */ fileStem.0 = i /* do something ... */ say fileStem.0 || ' lines read:' do i = 1 to fileStem.0 say 'Line ' || i || ' is "' || fileStem.i || '"' end /* do i = 1 to fileStem.0 */ exit
Be sure your datafile is structured as in the above example, with the EXTPROC statement on the first line. (Note that the EXTPROC statement must be the first text in the file or it will not work. Note also that the @Echo off statement, commonly included at the beginning of batch files, cannot precede the EXTPROC statement.)
Now you can simply call the datafile to call your REXX program to process it via:
MYDATA1
You can also add static parameters to the EXTPROC statement and/or use variable parameters when calling the data file.
The only disadvantage (you may call it a bug) of the EXTPROC feature is that the CMD.EXE always passes the name of the datafile without the path to your REXX program. Therefore either the directory with your datafile must be the current directory when calling the data file or you should use the workaround in the code below.
The workaround uses the fact that the CMD.EXE handles CMD files with EXTPROC statements like any other CMD file. This includes the processing of environment variables (for example %COMSPEC%) and parameters (for example %1, %2, etc.). Therefore we simply add the placeholder for the name of the CMD program, %0, as parameter for the EXTPROC statement. The %0 is always replaced with the name of the CMD as entered by the user.
An example datafile using this feature might look like:
EXTPROC myprog2 %0 /PARM staticParameter1 staticParameter2 datarecord 1 datarecord 2 datarecord 3
(The comments below, incorporated into MYPROG2.CMD, explain the use of the EXTPROC line shown in the MYDATA2.CMD example above and describe each parameter)
The REXX program MYPROG2.CMD to process this datafile might look like:
/* MYPROG2.CMD */ /* - sample REXX program for processing datafiles using EXTPROC */ /* */ /* The format of the EXTPROC statement for the datafile */ /* invoking this program is: */ /* */ /* EXTPROC MYPROG2.CMD %0 /PARM {staticParameters} */ /* */ /* MYPROG2.CMD is the name of the CMD to process the datafile. */ /* */ /* The string %0 is replaced by the name of the datafile as entered */ /* by the user. This is necessary, because MYPROG2.CMD needs it to */ /* detect the directory with the datafile if it is not the default */ /* directory. */ /* */ /* The string /PARM is necessary because without it (or something */ /* similar) MYPROG2.CMD cannot detect the begin of the static */ /* parameters if the name of the datafile contains blanks. */ /* */ /* {staticParameters} are parameters for the CMD which you can */ /* hardcode in the datafile. The number of static parameters is */ /* not limited. */ /* */ /* Assuming your datafile is called MYDATA2.CMD you can call it */ /* with */ /* */ /* MYDATA2 */ /* */ /* or, if you want to use variable parameter */ /* */ /* MYDATA2 varParameters */ /* */ /* The number of variable parameters is not limited. */ /* */ /* If MYDATA2.CMD is not in the current directory you can call it */ /* with an relative path like */ /* */ /* ..\MYDATA2 */ /* */ /* or also with an absolute path like */ /* */ /* c:\test\MYDATA2 */ /* */ /* In both cases you can add additional variable parameters if */ /* you like. */ /* */ parse arg thisParameter say 'This is MYPROG.CMD called with the parameters' say ' "' || thisParameter || '"' /* now split the parameter into filename and */ /* real parameter */ /* */ /* Note that we use the first parameter (that */ /* is the %0 from the EXTPROC statement) as */ /* name of the datafile and not the filename */ /* added by the CMD.EXE. This is necessary */ /* because the name added by the CMD.EXE never */ /* contains the path of the file. */ /* */ parse var thisParameter dataFile '/PARM' thisParameter dataFile = strip( strip( dataFile ), 'B', '"' ) if translate( right( dataFile, 4 ) ) <> '.CMD' then dataFile = dataFile || '.CMD' dataFileName = filespec( 'N', dataFile ) if translate( right( dataFileName, 4 ) ) <> '.CMD' then dataFileName = dataFileName || '.CMD' /* now remove the filename added by the CMD.EXE */ /* in Object REXX you can use: */ /* parse caseless var thisParameter tParm1 (dataFilename) tParm2 */ /* thisParameter = strip( tParm1 tParm2 ) */ /* in Classic REXX you must use: */ dataFileName = translate( datafilename ) i = pos( dataFileName, translate( thisParameter ) ) j = length( dataFileName ) thisParameter = strip( substr( thisParameter,1, i-1 ) || , substr( thisParameter,i+j ) ) say 'The parameter without the name of the datafile are:' say ' "' || thisParameter || '"' say 'The name of the datafile is:' say ' "' || datafile || '"' /* now read the data file */ fileStem.0 = 0 i = 0 do while lines( dataFile ) <> 0 /* ignore the EXTPROC line */ curLine = lineIn( dataFile ) if translate( word( curLine, 1 ) ) = 'EXTPROC' & i = 0 then iterate i = i+1 fileStem.i = curLine end /* do while lines( dataFile ) <> 0 */ fileStem.0 = i /* do something ... */ say fileStem.0 || ' lines read:' do i = 1 to fileStem.0 say 'Line ' || i || ' is "' || fileStem.i || '"' end /* do i = 1 to fileStem.0 */ exit
You can call MYDATA2 with one of the following statements to see how it works (assuming you've copied the file into the directory C:\TEST, C: is your OS/2 boot drive and you've copied MYPROG2.CMD to a directory available through the environment variable PATH):
MYDATA2 MYDATA2 varP1 varP2 varP3 c:\test\MYDATA2 c:\test\MYDATA2 varP1 varP2 varP3 c:\os2\dll\..\..\test\MYDATA2 c:\os2\dll\..\..\test\MYDATA2 varP1 varP2 varP3
The FORMAT command
If you want to redirect the input for the FORMAT command, you should use one of the following techniques:
"type format.dat | format C:"
or
" program | format C:"
format.dat must be an ASCII file containing the user input for the FORMAT program; program can be any OS/2 program that writes the necessary user input for the FORMAT program to STDOUT (for example a REXX program). You can't use the ECHO command to pipe the input for the FORMAT command.
Note: see also Navigate another program
OS/2 2.11 users may use the undocumented switch /NOF to force a quick format. This is equal to the /Q switch in the OS/2 WARP format program.
Source: The OS/2 WARP Survival Guide
The HELP command
The file that executes the HELP command in OS/2 is a batch file. Therefore, calling the HELP command from within a REXX program generates the error message SYS1803. To avoid this error you must use either
'CMD /C HELP' parameter
or
'HELPMSG ' parameter
(HELPMSG is the program called from HELP.CMD).
The SET command
It is tricky, if not impossible, to use the character = in environment variables used with the SET command. For example the command
SET test=myVar=myValue
leads to the OS/2 error SYS1003. To get around this limitation, surround the value with double quotes:
SET test="myVar=myValue"
Or you can set the environment variable in the CONFIG.SYS. SET statements in the file CONFIG.SYS don't have this limitation (except that you cannot use the equal char in the name of an environment variable).
But be aware that you cannot use nested environment variables in the CONFIG.SYS. Example:
REM *** Example SET statements for REM the CONFIG.SYS REM SET varA=ValueA REM *** The next statement does not REM work as expected if used REM in the CONFIG.SYS! REM SET varB=%varA% ValueB REM *** The next statement will REM only work in the CONFIG.SYS REM REM ---name---- ----Value---- SET configEnvVar=myVar=myValue
By the way: The characters |, < and > don't have a special meaning if used in commands in the CONFIG.SYS.
In any case, in REXX programs you should use the REXX function VALUE to get or set the value of environment variables. The function VALUE does not have this limitation.
(see also #Using meta chars)
The SHUTDOWN command / The SETBOOT command
Use the command SHUTDOWN to perform a system shutdown from the command line or a batch or REXX program. SHUTDOWN needs no parameter. You can also use the command
SETBOOT /IBD:x
where x is the drive you want to boot from next or a non-existing drive if you do not want to reboot the PC.
The difference between them:
Using SHUTDOWN OS/2 pops up a warning dialog for every open DOS or OS/2 session, whereas SETBOOT closes all DOS, Windows and OS/2 applications without any prompting.
Beginning with Fixpack #6 for WARP 4 there is another program to perform a system shutdown from the command line: TSHUTDWN.EXE.
TSHUTDWN.EXE does a normal system shutdown without pop up dialogs for OS/2 or DOS sessions. Note that you must unpack TSHUTDWN.EXE manually using the command
unpack TSHUTDWN.EX_
if you don't have a machine with WorkSpace on-Demand. The file TSHUTDWN.EX_ is in the fixpack directory \OS2.3.
According to a note from Armin Schwarz (see EMail Addresses) there are some restrictions using TSHUTDWN.EXE:
"The readme of Warp4 FP8 says to use /N to eliminate any prompts when shutting down. I found no difference between using /N or not using that option.
TSHUTDWN does shut down OS/2 window, DOS and seamless WIN-OS2 window sessions without prompting. BUT IT WILL PROMPT TO CLOSE A FULLSCREEN OS/2 SESSION.
TSHUTDWN must be used on a Warp 4 FP8 system. IT WILL NOT SHUTDOWN WITHOUT PROMPTING ON ANY OTHER WARP 3 OR 4 SYSTEMS. In other words, TSHUTDWN works just like SHUTDOWN on Warp 3 and Warp 4 (pre FP6)."
The SYSINSTX command
Use the command SYSINSTX.COM (normally in the directory \OS2\INSTALL\BOOTDISK or on the installation disk) to make an OS/2 drive or diskette bootable. Note that for using this command on HPFS drives the DLL UHPFS.DLL must be in a directory in the LIBPATH.
Source: The OS/2 WARP Survival Guide
The XCOPY command
In OS/2 WARP 4 you may get the error SYS1186 while trying to copy files with odd characters in their names (e.g. "ý1þ%Eþ.AXþ") using XCOPY. To resolve this problem, you must change the codepage to 437. To do this either issue the command CHCP 437 for a temporary change, or change the codepage line in the file CONFIG.SYS to CODEPAGE=437,850 and reboot the system to make the change permanent.
Please note that I cannot reproduce this error on my system with WARP 4 and Fixpack #1.
According to the OS/2 online help, XCOPY should return a return code of 2 if it cannot access one or more source or target files. In reality (OS/2 WARP 3 with Fixpack #17) XCOPY returns 0 in this case.
(Note: I've tried this again with WARP 4 Fixpack #5 - and at least in this environment XCOPY does return a return code of 2 in case of an error.)
To get around this bug, you can use a REXX program (see below) to call XCOPY, redirect its output to the REXX queue and check the contents of the REXX queue afterwards to get the results of the XCOPY command (see also The COPY command).
Warning: XCOPY is a version dependent program! Always use the XCOPY.EXE from the current active OS/2 version!!!
/* sample REXX cmd to get around some of the bugs of XCOPY */ /* init global variables */ sourceFiles = "*.*" targetFiles = "test1\*.*" /* flush the current REXX queue */ do i = 1 to queued(); parse pull; end; say "Now calling xcopy to copy " || , sourceFiles || " to " || targetFiles || " ..." /* execute XCOPY and redirect STDOUT and STDERR */ /* into the REXX queue (because the filenames go */ /* to STDOUT but the error messages go to STDERR) */ "@xcopy" sourceFiles TargetFiles "2>>&1" "| rxqueue" say " ... done. The RC of XCOPY is " || rc || "." say say "Now checking the result ... " say /* get no. of lines in the REXX queue */ qCount = queued() /* number of files copied */ filesCopied = 0 /* no. of files not copied due to source file */ /* access errors */ sourceFileErrors = 0 /* no. of files not copied due to target file */ /* access errors */ targetFileErrors = 0 /* no. of files not copied due to unknown errors */ unknownErrors = 0 /* error marker for the loop */ errorFound = 0 do i = 1 to qCount parse upper pull curLine curLine = strip( curLine ) if curLine = "" then iterate select when errorFound = "SourceError" then do sourceFileErrors = sourceFileErrors + 1 say " Error: XCOPY could not access the source file " say " " || curLine errorFound = "" end /* when */ when errorFound = "TargetError" then do targetFileErrors = targetFileErrors + 1 say " Error: XCOPY could not access the target file " say " " || curLine errorFound = "" end /* when */ when word( errorFound,1 ) = "UnknownError" then do unknownErrors = unknownErrors + 1 /* check if the next line is the name of a file */ if stream( curLine, "c", "QUERY EXISTS" ) <> "" then do say " Error: XCOPY retrieved the error " || , word( errorFound,2 ) || " accessing the file " say " " || curLine end /* if stream( ... */ else say " Error: XCOPY retrieved the error " || , word( errorFound,2 ) errorFound = "" end /* when */ /* SYS1186 - source file access error */ when left( curLine,7 ) = "SYS1186" then errorFound = "SourceError" /* SYS1187 - target file access error */ when left( curLine,7 ) = "SYS1187" then errorFound = "TargetError" when left( curLine,3 ) = "SYS" & substr( curLine,8,1 ) = ":" then errorFound = "UnknownError" substr( curLine,1,7 ) otherwise /* ignore status output of XCOPY */ if stream( curLine, 'c', 'QUERY EXISTS' ) <> '' then filesCopied = filesCopied + 1 end /* select */ end /* do i = 1 to qCount */ say say "XCOPY copied " || filesCopied || " file(s)." filesNotCopied = sourceFileErrors + targetFileErrors + unknownErrors if filesNotCopied <> 0 then do if filesNotCopied <> 1 then say filesNotCopied || " file(s) were not copied due to access errors." else say filesNotCopied || " file was not copied due to access errors." say " " || sourceFileErrors || , " file(s) because of source file access errors, " say " " || targetFileErrors || , " file(s) because of target file access errors " say " and " || unknownErrors || " file(s) because of unknown errors." end /* if filesNotCopied <> 0 then */ else do say " No errors found." end /* else */ exit 0
Hints for some DOS commands
This section contains hints and undocumented information about some DOS commands under OS/2. Note that the information provided here is. as far as I know, only true for the DOS included in OS/2 WARP.
Further note that information about the external family mode commands (these are external commands usable in DOS and OS/2 sessions) are included in the section Hints for some OS/2 commands.
If you're looking for more DOS secrets you may take a look at the website http://www.mdgx.com/secrets.htm.
Please be aware, that I do not know if the tips mentioned there are working in the DOS box of OS/2 also.
Thanks to Michael Lueck (see EMail Addresses) for this information.
The ASSIGN command
The DOS command ASSIGN included in OS/2 supports the nearly undocumented switch /S to display the current assignment status. ("nearly undocumented" because ASSIGN /? shows this switch, but you can't find anything about it in the online help)
Source: The OS/2 WARP Survival Guide
The DATE command
Use the undocumented switch /N of the DOS command DATE to display the date on the screen without prompting to enter a new date.
Source: The OS/2 WARP Survival Guide
The DIR command
Use the switch /R of the DIR command to get the long name of a file in a DOS session (works only for files and directories that have the EA .LONGNAME set)
Example output of DIR with the /R switch:
BMT_MICR 134173 29.05.96 11.28 BMT Micro Shareware Catalog
The EXIT_VDM command
Use the DOS command EXIT_VDM to close a DOS session independent from the number of called nested command processors (EXIT only closes the last called command processor).
Source: The OS/2 WARP Survival Guide
The ISWINDOW command
Use the return code of the DOS command ISWINDOW to distinguish between windowed and fullscreen DOS session (0 = fullscreen, 1 = window). Example:
@ECHO OFF REM *** sample DOS batch to distinguish REM between window and fullscreen REM --- call the util program iswindow if errorlevel 1 goto IsWin :IsFS SET sessionType=FS ECHO. Running in a DOS Fullscreen session GOTO NEXT :IsWin SET sessionType=WIN ECHO. Running in a DOS window session :NEXT
The MODE command
The MODE called with the parameter co#,# to set the window height and width command accepts any combination of lines and columns as long as the formula "lines x columns" does not exceed 8192.
Example:
mode co70,112 mode co160,51
You do not have to use the co, e.g.
mode co160,51
is the same as
mode 160,51
Source: Found in a message in a public news group
The READLINE command
Use the nearly undocumented DOS command READLINE to read a line from the keyboard and place it in the batch parameter variables. Use the parameter /V for READLINE to convert the input into upper case. ("nearly undocumented" because READLINE /? works but you can't find anything about it in the online help)
Note: READLINE reads from the keyboard - not from STDIN.
Example:
@ECHO OFF REM *** DOS batch file ECHO. Type something to test the READLINE function: READLINE ECHO. You typed: %0 %1 %2 %3 %4 %5 %6 %7 %8 %9
Source: The OS/2 WARP Survival Guide
The RENAME command
Use the undocumented switch /S of the DOS command RENAME to rename directories in DOS sessions also. The OS/2 command RENAME can rename directories without this switch.
Source: The OS/2 WARP Survival Guide
The SET command
Use the undocumented switch /S of the DOS command SET to get the number of remaining free bytes in the DOS environment. Note that you do not need this option in OS/2 sessions because the size of the environment of OS/2 sessions is dynamic.
Source: The OS/2 WARP Survival Guide
The TIME command
Use the undocumented switch /N of the DOS command TIME to display the time on the screen without prompting to enter a new time.
Source: The OS/2 WARP Survival Guide
The TRUENAME command
Use the nearly undocumented DOS command TRUENAME to display the true name of a file on the screen. For example the command
truename c:\os2\dll\..\install\..\..\config.sys
will print C:\CONFIG.SYS to the screen. ("nearly undocumented" because TRUENAME /? works but you can't find anything about it in the online help)
Source: The OS/2 WARP Survival Guide
In OS/2 REXX programs you may use the function STREAM with the third parameter set to QUERY EXISTS to achieve the same result for existing files.
Reserved directory & file names
You cannot create a directory or file called \PIPE. This is a necessary restriction, because the names of named pipes all begin with \PIPE\ (and pipes are handled like ordinary files in OS/2).
Also you cannot create or access directories or files with the names of loaded device driver in any directory. Some standard device driver for OS/2 are the following (there may be others not listed here):
Device name | Device driver |
---|---|
APM$ | |
CLOCK$ | CLOCK01.SYS |
COM1 - COM4 | COM.SYS |
CON | |
DOS$ | DOS.SYS |
IBMKBD$ | IBMKBD.SYS |
KBD$ | IBMKBD.SYS |
LOG$ | LOG.SYS |
NUL | (NUL device) |
LPT1 - LPT9 | PRINT01.SYS |
MOUSE$ | MOUSE.SYS |
OEMHLP$ | SCREEN01.SYS |
PCLOGIC$ | PCLOGIC.SYS |
POINTER$ | POINTDD.SYS |
PRN | PRINT01.SYS |
RESERVE$ | RESERVE.SYS |
RESMGR$ | TESTCFG.SYS |
SCREEN$ | SCREEN01.SYS |
TIMER$ | CLOCK01.SYS |
SINGELQ$ | PMDD.SYS |
In general you should not use a file or directory name ending with a dollar sign.
Warning
You can create a directory or file with the name of a device if the necessary device driver for the device is not loaded. But be aware, that you cannot access that file or directory anymore after installing the device driver again!
(see also Output & Input control, Reserved names for files in REXX, Check if a name describes a device or a file and Using the CLOCK$ device.)
If you really need to create or delete a directory with the name of a device you can use the following trick:
REM *** Create a directory called APM$ in the current directory md APM$\. REM *** Delete a directory called APM$ in the current directory rd APM$\. REM *** Create a directory called CLOCK$ in the root directory md \CLOCK$\. REM *** Delete a directory called CLOCK$ in the root directory rd \CLOCK$\.