[Chaos CD]
[HaBi 2]    Batch magic
[Gescannte Version] [ -- ] [ ++ ] [Suchen]  

 

Batch magic

DOS batch processing is notoriously slow, but it does have its uses. John DeHaven provides a compendium of tricks, techniques and curiosities for your reference.

What is the world's slowest interpretative language? This dubious honour surely belongs to DOS batch processing, which runs like granny. Let's go ahead and stretch a point and call DOS batch processing a language. It is useful to think of the statements of this language as including the traditional batch processing commands, all DOS commands and the names -of any executable files (including other batch files) that are available in the default directory or via PATH.

Unfortunately, even if we generously expand the definition in this way, DOS batch processing still isn't a complete language, since it is not interactive and cannot add or even count. Even so,if you hold your jaw a certain way there is plenty of unsuspected power to be found. We'll explore generation and passing of variables, file creation, true calls with return, giant loops under control of 'FOR' and recursion. Batch processing is not really as boring as IBM and Microsoft try to make it seem.

The traditional (IBM/Microsoft) discussion lists only six batch processing commands, and we'll assume you have a working knowledge of these: ECHO, FOR, GOTO, IF, PAUSE, REM and SHIFT. Of these, ECHO is useful outside of batch processing, and FOR can be used outside batch processing to do some non-trivial things. Four other commands, COMMAND, CLS, EXIT and SET - are'not usually mentioned as part of batch processing, although they would rarely be used anywhere else. We will use them all here.

Speed

One reason batch processing runs so slowly is that it makes a disk call for each line of code. Evidently then, a given batch file will run faster if you can reduce the number of lines in it. After you have a batch file running, you may be able to reduce the number of lines by combining some of them in a FOR structure. The following rules apply:

1 If statements have no argument,

they may be combined;

2 If statements have the same argu

ment, they may be combined; and

3 Statements with the same argument may also be combined with statements with no argument.

Here are some examples to illustrate the above points:

FOR %%A IN (CLS VER VOL BREAK VERIFY SET) DO %%A

FOR %%A IN (CLS A: ECHO PAUSE CLS) DO %%A Insert back-up

diskette

FOR %%@ IN (CLS C: IF A:) DO %%@ EXIST PP. BAT PP

FOR %%@ IN (MD CD) DO %%@ SDIR1

These work because certain commands like CLS and PAUSE (and certain other executable files you might have created) do not take any arguments, so when FOR expands them with an argument, the argument is ignored.

Batch files will run much faster if you allocate extra 'buffers' with CONFIG-SYS. Briefly, this specification allocates memory for disk 1/0, one buffer per cluster read from diskette. When a disk call is made, these buffers are checked first, and if the record is already here, no physical disk reference will be made. The overhead cost is about 1k per buffer (not 512k as IBM states), above the default two buffers. Here is how you can be sure you have those extra buffers activated:

1 The disk from which you boot

must contain a file called:

'CONFIG.SYS.'

2 This file must contain the state

ment:

BUFFERS=9 although a larger number will be OK.

3 For good measure, include the fol

lowing two lines:

FILES=99

DEVICE=ANSI.SYS

The first of these will allow a much larger number of file handles to be opened than the default eight, at a cost of only 3783 bytes of memory. Many programs need more handles than eight, and you'll be disappointed in the performance of some of the following examples if you don't allow this extra latitude. As for the assignment of the ANSI.SYS

driver, 1 can't imagine anyone with more than 16k of RAM not w31hting this, because it allows control over screen colours and attributes, arbitrary cursor positioning and even keyboard reassignment (16-character keyboard macros at the DOS level without a fancy program!)

If you are echoing many lines to the displa y, you will find that it is much faster to put in a single line to TYPE a file that contains your multiline display. This will cost some disk space, since you will have to create this extra file. 1 usually denote such files as SCR (for 'screen') files.

If you use labels in a batch file, those that are most likely to be called should be put near the beginning, as the batch processor scans the entire file from the beginning every time it looks for a label.

Control

Whether you use ANSI.SYS or not, the display understands the sequence ESC+2-J to mean 'clear the screen'. The CLS command in fact sends just this sequence to the screen. This means that you can include this string in any statements you ECHO to the display and in any file that you might TYPE to the display. If you write your batch files with an editor that allows the insertion of the ESC code, you will be able to echo ANSI control sequences from the batch file. Some very nice effects are available. If we let the expression '^V stand for the ESC code, the sequence

CLS

ECHO [[2Jlnstall the back-up diskette in drive A. G U5m PAUSE

ECHO ^[[2K^[10;lm will clear the screen and display the prompt message followed by a beep and a blinking pause message. When a key is struck, the blinking pause message is wiped out.

You could output blank lines with ECHO followed by two or more spaces with DOS 2.x, but this no longer works with DOS 3.x. The sequence ESC-space- <255fl> will work with DOS 3.x, the idea being to echo the invisible character 255 hex. On an IBM machine you can get this character if you hold the ALT key and press 2-5-5 on the numeric keypad. On other machines, or with certain editors, the procedure may be different.

You may want to shut up the display at some stage. To do this you set ECHO OFF so that you won't see the batch procedure running, but certain commands still natter at you. The bit bucket 'file' NUL may be used for this. If your file contains the statement

DEL *.BAK>NUL then it will attempt to delete all backup files. If there are none, the error message will be redirected into the 97th dimension and will not be seen. You could also get the effect with the statement

IF EXIST *.BAK DEL *.BAK but this would require a bit of extra time for the existence test. IF EXIST only works for files in the current drive and directory, which is sometimes a bother.

Variables

There are four kinds of variables in batch processing. %0 returns the name of the batch file itself while %1, %2, %3 represent tokens passed in on the command line after the name of the batch file. %%A, where 'A' may be any character, is the form of a variable that takes successive values of the 'IN' portion of a FOR statement.

The usual literature does not make it obvious that a variable in the form %WORD% will return the value of a variable called 'WORD' that has been set into the 'environment'. To install such a variable, you execute a command, in or out of a batch file, of the form

SET VAR=SOME STRING where VAR is the variable name, and the value is any string.

To see how SET works, try the following batch program.

ECHO OFF

SET X= NOT

ECHO THIS DOES %X% FAIL.

SET X=

ECHO THIS DOES %X% FAIL.

These variables set into the environment are made available to all executable programs, and this is how they are accessed by BAT programs.

Often you may need to control batch file behaviour according to whether a variable exists or not (regardless of its value). The IF statement does not directly test for this; you must supply some minimal string on both sides of the

operator. I'll use a minimal string of '@' to show the two basic kinds of existence tests.

Executes if the variable %1 exists:

IF NOT %1@==@ ...

Executes if the variable %1 does not exist:

IF %1@==@ ...

Later we'll see some other uses for these techniques but, as an example, suppose you have a program that becomes memory-resident when called, and if called again will install another copy of itself, gradually eating up your available memory (some otherwise excellent commercial products have been known to behave like this). The program is not used every day, and is too large to install no matter what with AUTOEXEC.BAT. What you need is a batch program that calls this maverick program if it is needed, but only once per session. Let's suppose our resident-type program is called DBSORT. A batch file fragment that would do the trick might be:

IF NOT %SORT%@==

INS@ DBSORT IF NOT %SORT%@==

INS@ SET SORT=INS

After DBSORT is installed once, the variable SORT is set to INS in the environment and, therefore, DBSORT will not again be called until the machine is rebooted.

Creating a file with a batch file

The command 'ECHO This is a test message >TEST.TX7 will create a one-line file-named TEST.TXT which contains the words 'This is a test message'. One reason to do this would be to set a flag that will last between sessions. Things set in the environment go away with the power, but a temporarily-created file will not, and its existence may be tested by the IF EXIST statement of batch processing. Your AUTOEXEC.BAT might want to set up a large print spooler if you have a dot-matrix printer installed, and omit the spooler if a daisywheel unit is attached. The following statement in AUTOEXEC.BAT would do it, based on the existence or not of a file called DAISY.

IF NOT EXIST DAISY BIGSP0OU128

At some point in your configuration procedure you could create the flag file if required with the statement:

ECHO Daisywheel printer

installed>DAISY

You can create a temporary file and then use the temporary file to answer a question. Two commands that are hard to automate are DEL and PRINT, because under certain conditions they ask questions of the user. The following batch sequences will proceed without pause'.

ECHO Y >YES DEL <YES ECHO IPT1 >PSPEC PRINT %1 <PSPEC

DEL PSPEC

In each case, if the procedure asks a question, it finds a file waiting with the answer, and it takes the answer from the file.

A multiple line file may be written a line at a time, by using the ">>' operator, which adds a line to a file. '>>' creates the file if it doesn't yet exist. The following sequence writes a three-line file (try it).

DEL TEMP

ECHO This is the first line

>>TEMP ECHO This is the second line

>>TEMP ECHO This is yet another line

>>TEMP TYPE TEMP

You could even write another batch file this way and then execute it! Here is how to create a program that keeps an activity log. First create a file that contains only a carriage return and a line feed by the following procedure:

POPY CON CRLF.BAT <return> <return>

<CTRL-Z~:~. <return>

We've named this weird little fil e CRLF.BAT because there is another important use for it that we'll discover below. One use for this will come clear if you try

DATE <CRLF.BAT and then

DATE <CRLF.BAT >LOG TYPE LOG

This, then, would be your activity log program fragment. It records a date and time in file LOG whenever it runs:

DATE <CRLF.BAT >>LOG TIME <CRLF.BAT >>LOG For maximum speed we compress this to:

FOR %%@ IN (DATE TIME) DO

<CRLF.BAT >~>LOG

You could also use this technique to put data in a file. Below we will see how a batch file could read such data.

Chaining

As is well-known, if you name another batch file in a batch file, the next batch file begins executing. In this way batch files may be chained. This chaining can be used to cause an abrupt exit from a long batch file that runs slowly. Suppose the batch file has the following structure:

:LABEL1

<FIRST PROCEDURE> GOTO EXIT

:LABEL2

<SECOND PROCEDURE GOTO EXIT

:LAST LABEL

<LAST PROCEDURE>

:EXIT

This is likely to execute slowly because after any given procedure is executed, EXIT is called and the batch processor must read the whole file from the beginning to find the label in the very last line. If the donothing file CRLF.BAT is still available to DOS, the preceding program may be considerably speeded up by writing it in the following form: :1LABEL1
<FIRST PROCEDURE~> CRLF :LABEL2 <SECOND PROCEDURE CRLF

:1LASTLABEL

<LAST PROCEDURE>

Now, instead of searching the file for ":EXIT", the program will directly chain to CRILF and abruptly quit.

Using SHIFT

There are two good uses for the SHIFT command: to allow an indeflnite number of command line parameters and to count. Suppose you have a print formatter called PRT.COM. You could feed several files to it with a batch file containing the following:

FOR %%@ IN(%`] %2 %3 %4 %5 %6 %7 8% 9%) DO PRT %%@ This is fast enough, but is limited to nine arguments. This little program will accept unlimited arguments, using SHIFT.

:DO

IF %1@==@ GOTO ENDDO

PRT %'I

SHIFT

GOTO DO :ENDDO or quicker: :PROC

IF %1@==@ GOTO ENDPROC

PRT %1

FOR %%@ IN(SHIFT GOTO) DO

%%@ PROC

:ENDPROC

This is the most general form that will not execute if there are no arguments. A shorter (and therefore faster) version of this basic loop may be used, but this form will execute at least once, even if there are no arguments. Use it by all means if this does not matter.

:LOOP

PRT %'I

SHIFT

IF NOT %1@==@ GOTO LOOP or quicker: ; LOOP

PRT %1

FOR %%@ IN(SHIFT IF) DO %%@

NOT %1@==@ GOTO LOOP

If we can use CRILF.BAT to break out of the program, we can have the best of both worlds.

:LOCIP

IF %1@==@CRLF

PRT %1

FOR %%@ IN(SHIFT GOTO)

DO %%@ LOOP Soon we'll see a more advanced application of this principle. You can also use the command-line tokens as items to be counted. Write TEST.BAT: ECHO OFF CLS :DO

ECHO Display for token %1

SHIFT

IF NOT.% 1 @= =@ GOTO DO

Run TEST with several calls to see this work. TEST TEST 1 2 3 TEST X X X TEST NOW THREE WORDS TEST 1 TW032+2 56789TEN 11

Batch procedures

You may want to create a complex batch file to automate an obnoxious procedure, but perhaps you don't use it often enough to remember its complex call syntax.

The answer to this is to set the batch file up so it will give you some instructions if called with no arguments. For example, here is the start of my batch program 'DLOAD.BAT' which permits unattended downloading of partitioned datasets from the IBM mainframe, a procedure that could take hours. The actual download procedure is so slow that batch file speed is a negligible factor, so nothing is compressed into FOR loops here. ECHO OFF CLS

IF NOT 1/0@==@GOT0 START

ECHO DOWNLOAD PARTITIONED DATASETS FROM MAINFRAME ECHO

ECHO SYNTAX: DLOAD DSN DIR MEMBER1 MEMBER2 MEMBER3...

ECHO Where DSN is the fully qualified dataset name,

ECHO DIR is the

destination subdirectory.

ECHO and MEMBERn are any number of member names.

CRLF :START SET DSN=%l SHIFT SET DIR=%l FOR %%@ IN(MD SHIFT) %%@~%l MD\%l >NUL SHIFT :DO

IF %1@==@ CRLF

<DOWNLOAD PROCEDURE>

SHIFT

GOTO DO

Several techniques are used in this program. If DLOAD is entered with no arguments, the first IF statement detects this, and the instructions are

echoed. When DLOAD is called with arguments, the first variable is set to %DSN% for later use by <download procedure>, then is shifted away. The second variable (now %1) is stored as %DIR% and then creates the desired subdirectory before banishment by shifting. The 'members' are shifted in turn into position %1 by the loop, until they are all used up. Exits from the program are by fast calls to CRLF.BAT, which was created earlier. If the attempt to make the subdirectory fails, perhaps because the subdirectory already exists, the resulting error message will be shunted off to NUL.

Menus

Fig 1 shows a program to control some settings for an Epson/IBM-type printer. It will display a menu if called without argument, but this menu may be bypassed if the user knows what to enter.

Calling batch.files

Now we will see how we can call another batch file and return from it, as though it were a subroutine. If you chain to another batch program, that's it - there is no return. The secret of true calls is the 'COMMAND' statement.

'COMMAND' loads another copy of part of COMMAND.COM into memory and gives it control. This does not consume as much memory as IBM would have you believe, since it does not load another copy of the whole 28k or 40k COMMAND.COM - it only loads another copy of the command pricessor which is about 4k. The new command processor runs quite independently of the previous one.

The command 'EXIT' purges the currently executing command processor and puts you back to the previous one. EXIT does nothing if entered into the one and only original command processor.

It is not obvious what the use of this is until you remember file redirection. What happens if the new command processor takes its input from a file? Try it by making a file full of commands, ending with EXIT. We'll call it GIZMO. (If you don't end this file with EXIT you'll never return; the computer will hang up for good.) VOL

ECHO This line is from

the called file. VER EXIT

Next create DRIVE.BAT and run it: ECHO OFF CLS ECHO This line is from

the main program.

COMMAND <GIZMO

ECHO This line is again

from the main program.

This illustrates the general principles. We can vastly improve on this, though. The special form:

COMMAND/C string

says, in effect, to invoke a new command processor, feed it 'string' as an input command, execute the command, then EXIT. If we feed a command processor a batch file name, it executes the batch file. Because of this we can rename GIZMO to GIZMO.BAT and drop the EXIT command from the end, thereby converting it into a plain vanilla batch file. Change DRIVE.BAT as follows:

ECHO OFF

CLS

ECHO This line is from

the main program.

COMMAND/C GIZMO

ECHO This line is again

from the main program.

This is almost the effect we want. We are spared the installation message from the command processor, but the secondary command processor echoes everything. Even if you put ECHO OFF at the beginning of GIZMO.BAT it will still echo the first prompt and the ECHO OFF. If it is really important to silence everything, you can use redirection. Change the programs as follows:

GIZMO.BAT:

VOL >CON

ECHO This line from the

called file. >CON

VER >CON

DRIVE.BAT:

ECHO OFF

CLS

ECHO This line from the

original batch program

COMMAND/C GIZMO >NUL

ECHO This line again from

the called program

The trick here is to send all output from the secondary command processor into NUL. Then we override this in the called batch file with redirections to CON for everything we really want tQ see. (More examples

on this are given below.)

This call/return procedure can be nested to any depth that your memory allows, and you can play tricks with variables. Try these three batch programs. MAIN.BAT ECHO OFF CLS

ECHO MAIN here. Are you watching? COMMAND/C SUBFILE1 file speak sub %1 >NUL

ECHO Whew! We made it back to

MAIN again.

SUBFILEII.BAT

ECHO This is %3%1 1 %2ing. )CON

COMMAND/C %3%12 %1 %2 %3 %4

ECHO Goodbye from %3%1 1. >CON

SUBFILE2.13AT

ECHO Now %3%1 2 %2s. >CON IF NOT %4@==@ ECHO

What does "%4" mean? >CON

Try launching this collection with 'MAIN'and 'MAIN AXOLOTL'.

More practically, suppose I have a lot of programs to download from the mainframe with DLOAD.BAT. What I want are several members from each of several partitioned datasets. This whole procedure might take all night - I plan to submit a huge metabatch file when I go home in th e evening. I can create a driver for DLOAD.BAT and off we go: COMMAND/C DLOAD BNW.TE.CLIST

TECLIST M1 M2 M3 M4 ... COMMAND/C DLOAD BNW.TE.SAS

TESAS M1 M2 M3 M4... COMMAND/C DLOAD BNW.TE.

TABLES

TETABLES M1 M2 M3 M4 ...

If nothing goes horribly wrong, I should return in the morning to find the selected members neatly copied into appropriate subdirectories.

Recursion

By now you may be saying 'All that is very well, but if a batch file can be made to call another file, what would happen if you asked it to call itself or maybe call another batch file that called the first one, or maybe

Being of an inquisitive nature 1 explored some of these questions. The answer, in general, is that you can have any number of recursive chains or calls, so long as memory and file handles are available. If you are careful of counts and end conditions, you won't get in too much trouble. On the other hand, if one of these were to run away ...

To ease into this subject, we'll consider recursive chaining first. Recursive chaining is an alternative to SET that initialises variables for further use by the program. The differehce is that this way the program sets %1 through %9, so an operation like SHIFT might be used against them. Nothing fancy is needed for recursive chaining. Consider a file called CHAIN.BAT:

ECHO OFF

CLS

IF %1@==@ CHAIN 1 23456789 :DO

ECHO <DO SOMETHING WITH FILIE%l>

SHIFT

IF NOT %1@==@ GOTO DO

Here is a catalogue printer for your hard disk. Your various subdirectories are 'remembered' in the recursive call statement.

CAT.BAT

ECHO OFF CLS

IF %1@==@ CAT DBASE LOTUS

ORD WRK C CD\ DIR 1 SORT >PRN :LOOP

DIR \%11 1 SORT >PRN

FOR %%9 IN(SHIFT IF) DO %%@ NOT %1@==@ GOTO LOOP

CAT.BAT will print catalogues for any arbitrary' selection of directories if called like this:

CAT DIR1 DIR2 DIR3 ...

Suppose you have a file card ACTION.DATA which expects to find data in the form of tokens in a file called DATA.BAT. Possibly DATA.BAT was generated by another program which could be another - or even this - batch file. DATA.BAT contains a statement as follows..

ACTION DATA1 DATA2 DATA3... ACTION.BAT starts as follows;

IF %1@==@ DATA

As we can see, if ACTION.BAT is called with no arguments, it will immediately chain to DATA.BAT which calls ACTION right back, passing DATA1, DATA2, DATA3 ... to it as %t%2, %3 ...

So far 1 haven't been able to think of something 1 needed to do with batch processing that couldn't be done more easily some other way. No doubt the Lisp-wallahs out there will immediately think of several important applications. On the other hand, this may be onja of those case which vividly illustrates the difference between what you get away with and what's useful.

 

  [Chaos CD]
[HaBi 2]    Batch magic
[Gescannte Version] [ -- ] [ ++ ] [Suchen]