These are the executable utilities included with the MSDOS package. They are free standing programs in their own right, but some of them are useful in batch files because they do something interesting or because they can be used to extract information from the system or user. Most of the .COM and .EXE files in your \DOS directory fit the general description, but only a few have proven to be useful for information gathering. Interestingly enough, COMMAND.COM falls in the useful for doing things and useful for getting information categories.

CHOICE
is an executable that returns key information as ERRORLEVEL. It is discussed in
the DOS 6 HELP program, and will not be addresseed here. Rather, I'll show a
somewhat slicker approach using a pair of tiny executables I worte some years
ago that behave very differently from CHOICE. The "Batch and DEBUG" section
contains batch files to create both programs. KPAUSE and KPAUSED don't actually
pause to read keys - they read them on the fly, so you can press a hot key
anywhere during the batch file before they run to have them act on the key. The
former does not consume the key, so that it can be tested with another program
later, or even passed through to the DOS prompt at the end of the batch file.
This allows typing the next command and having the batch file act on its
knowledge of what is to happen after it terminates. KPAUSED does consume a
single key press. I use these in AUTOEXEC.BAT to provide invisible means for
aborting various parts of the initialization code: <Space> aborts complety,
immediately after setting up the path and prompt, while any key prevents
launching Windows. Since the Windows abort key may or may not have been pressed
by the first test, the first test does not consume the key, unless it is
<Space>. The code shown here is drastically purely for demonstration - it
includes two DIR commands to generate delays and indicate the paths through the
code (in AUTOEXEC.BAT, the key can be pressed any time after the BIOS has
activated the key buffer, as during CONFIG.SYS).
@echo off
dir c:\dos
kpause
if errorlevel 32 if not errorlevel 33 goto done
dir c:\dos
:done
kpaused
if errorlevel 1 goto end
win
:end
IF ERRORLEVEL n IF NOT ERRORLEVEL n+1
isolates a specific value of ERRORLEVEL - the test is basically an equals or is
qreater than test.
CHOICE can be used to implement menus - this is a trivial example that shows one way to structure a single level menu.
@echo off
:loop
cls
echo A) Directory of C:\ B) DOS Version
echo C) Current Time and Date D) Exit
echo.
echo Enter the letter corresponding to your selection:
choice /c:abcd /n > nul
if errorlevel 4 goto end
if errorlevel 3 goto menuc
if errorlevel 2 goto menub
REM the A selection falls through to here
echo.
dir c:\ /w
echo.
pause
goto loop
:menub
echo.
ver
echo.
pause
goto loop
:menuc
echo.
echo. | date | find "Cu"
echo. | time | find "Cu"
echo.
pause
goto loop
:end
The structure of a multi-level menu is a bit more complex since we have to include code to jump between levels.
@echo off
:loop1
cls
echo A) Next menu level B) Exit
choice /c:ab /n > nul
if errorlevel 2 goto end
REM A selection falls through to here
:loop2
cls
echo A) Directory of C:\ B) DOS Version
echo C) Current Time and Date D) Top level menu
echo.
echo Enter the letter corresponding to your selection:
choice /c:abcd /n > nul
if errorlevel 4 goto loop1
if errorlevel 3 goto menu2c
if errorlevel 2 goto menu2b
REM the A selection falls through to here
echo.
dir c:\ /w
echo.
pause
goto loop2
:menu2b
echo.
ver
echo.
pause
goto loop2
:menu2c
echo.
echo. | date | find "Cu"
echo. | time | find "Cu"
echo.
pause
goto loop2
:end
The general idea
is to display a list of selections and their selector letters, then to detect
which selection the user made and to jump to the code that handles that
selection. Sometimes, it is desirable to return to the menu when the code for a
selection has finished, sometime it isn't. The examples illustrate returning to
the menu, but they can be converted into the exit on completion form by
replacing the target of the GOTO at the end of the block with end
instead of the name of the current loop so that the loop does not restart.
If something more sophisticated is needed, there are a couple of options:
|
|
use ANSI.SYS and Esc sequences to control the cursor and text color in conjunction with a utility that allows reading the arrow and Enter keys |
|
|
use a third party menu utility that has all the necessary functions built in |
If it is menu is part of a package that is to be distributed to other users, it is necessary to somehow make sure that all the pieces are available - it is seldom possible to insure that ANSI.SYS is installed, and while there are replacements for it that can be loaded from the batch program, their use is not generally advisable. Third party menu programs tend to be old and unaware of modern operating systems. All in all, the simplest appraoch is most likely to work, with the caviat that NT doesn't have CHOICE installed by default and it is not freely distributable, though some third-party key readers are. It is conventional to use a third-party key reader even when the target system is known not to be NT - actually, CHOICE is available only in a couple of generations of DOS (5.0 and later) and in Win9x and so should not be considered universal.
COMMAND
/e:nnn /cFOO
is useful both in batch files and from the command line to spawn a secondary (or
tertiary, or later)
command processor
with a specified
environnment
size (in bytes specified by nnn in the /e:nnn switch), and have it execute the
program specified in the /c switch. It was several effects and side effects,
notably that it does get a new environment, distinct from the parent
environemnt, and that the output of the command processor can be
redirected
to a file or device, which, if the program is a batch file that has not turned
echoing off, will redirect the command display from the batch file (see:
Debugging Batch Files).
UNDELETE
Now here's an oddity - unlike the other DOS utilities, UNDELETE writes
its error messages to
STDOUT
instead of
STDERR.
This means that we can pipe the *errors* through FIND and take action based on
them. The user need not deal with the error messages or even see them. One of
the most useful UNDELETE syntaxes is the one with the /list switch: UNDELETE
d:\*.* /list. With this you can tell
if a drive or directory exists,
- even on a Novell LAN drive (if exist d:\foo\nul fails there and other places).
You can even find out if the drive is a physical or logical drive and whether it
is SUBSTed or is something else (CDs and LAN drive look alike)
These tricks tend to fail in WIN95, NT, and OS/2, but those OSs are outside the scope of this book.
This code determines whether a directory exists on the
given drive. It is unusual in that it doesn't fail with CDs, Novell (and other)
LAN drives, or even empty floppy drives. It does fail under WIN95, NT, and OS/2
(or so I'm told), but those are outside the scope of this work. The second
example is more extensive than would normally be needed because it also extracts
whatever information it can about the nature of the drive. Both the directory
checker and the drive type identifiers use mostly well known and understood
techniques - notably the technique of redirecting the output of FIND to a file
and attempting to copy the file in order to tell if it contains something or
nothing (COPY will not copy an empty file).
@echo off
echo a| undelete %1\*.* /list | find /i " %1" > %temp%\}1{.dat
copy %temp%\}1{.dat %temp%\}2{.dat > nul
del %temp%\}1{.dat
if not exist %temp%\}2{.dat goto no
echo Path exists
del %temp%\}2{.dat
goto end
:no
echo Path not found.
:end
The key to the function of that example is the fact (mentioned above) that UNDELETE does not observe the MS convention that error messages should not be redirectable (and the user should therefore not be able to do uninteresting things like create error log files) - this is understandable since the program wasn't written by Microsoft (it comes from Central Point Software. The information of interest may in fact in the error messages (as below). This example forces its transient files to be created in the directory given in the TEMP environment variable - if this is not set at all they go into the root of the default drive (not the best of places). The copy command is redirected to NUL to surpress display of the "One file copied" message
The, perhaps perplexing, "ECHO a" piped to UNDELETE is there in case of removable media that are in fact removed (empty floppy or CD drive) - it responds to the ARF prompt with an "A" for "abort".
One interesting point that is not exactly a batch language issue is left for the reader: What happens if the drive is a CD containing an audio disk?
This related code tells what can be determined about the
drive from UNDELETE.
@echo off
set type=Does not exist
echo a| undelete %1\*.* /list > %temp%\}1{.dat
find /i " network" < %temp%\}1{.dat > %temp%\}2{.dat
copy %temp%\}2{.dat %temp%\}x{.dat > nul
del %temp%\}2{.dat
if not exist %temp%\}x{.dat goto notnet
set type=Network or CD (non-FAT drive)
del %temp%\}x{.dat
goto done
:notnet
find /i " subst " < %temp%\}1{.dat > %temp%\}2{.dat
copy %temp%\}2{.dat %temp%\}x{.dat > nul
del %temp%\}2{.dat
if not exist %temp%\}x{.dat goto notsubst
set type=SUBSTed drive
del %temp%\}x{.dat
goto done
:notsubst
find /i " abort " < %temp%\}1{.dat > %temp%\}2{.dat
copy %temp%\}2{.dat %temp%\}x{.dat > nul
del %temp%\}2{.dat
if not exist %temp%\}x{.dat goto notempty
set type=Empty removable media drive or drive error
del %temp%\}x{.dat
goto done
:notempty
find /i "invalid" < %temp%\}1{.dat > %temp%\}2{.dat
copy %temp%\}2{.dat %temp%\}x{.dat > nul
del %temp%\}2{.dat
if not exist %temp%\}x{.dat goto noterr
set type=Drive not found or unreadable drive
del %temp%\}x{.dat
goto done
:noterr
set type=Local hard or floppy drive
:done
del %temp%\}1{.dat
echo %type%
:end
Some other information can be extracted, but I do need to leave something for the reader to tackle on his/her own. For example, if the drive is local and FIND reports that A: or B: was found in the file, it's usually safe to assume that the drive is a floppy (but not the other way around - I frequently encounter machines with floppies for C: and/or D:). The reader is invited to explore and experiment - as long as you make sure to use the /list switch, UNDELETE is pretty harmless.

The intent of LOADFIX is to eat up the first 64K of memory so that certain programs that missuse segment wraparound or otherwise can't run if loaded in the first 64K of memory can be run. The program just eats the memory and executes whatever program it was passed as its first argument and passes that the rest of the command line. It would appear that LOADFIX would be of little interest to batch programmers, but that is far from the case: it provides the core of a solution to the long standing problem of executing a command on a list of items (e.g., a list of directories or files) because the program to execute can be a batch file.
My plan was to explore the list processing idea here, but it grew too large and has been moved to its own "List Processing" page.
