BATCH FILE TIPS AND UTILITIES The Tenth in a series by Mitchell A. Hoselton WHEN ONCE IS ENOUGH There are many PC tasks that most users only want or need to do once a day. Imagine an AUTOEXEC.BAT file that includes a password utility, prints out the overnight E-mail, the daily appointment calendar, and a list of important events on this date in history, runs an elaborate hardware diagnostic with several more pages of printouts, downloads the latest news from CompuServe, synchronizes the DOS clock with the atomic clock at the U.S. Naval Observatory in Washington, D.C., et cetera. Now, imagine how frustrating it will be for anyone who must reboot this machine several times one day while optimizing or debugging a new command in CONFIG.SYS. It's slow, and irritating. That's what I call the once-a-day dilemma. With the possible exception of the password program, all of the tasks listed above require a once-a-day execution schedule. There are a couple of ways to prevent selected start-up activities from running every time the PC reboots. First, there is the small utility called ALREADY.COM. It edits its own code to include the date the first time it runs each day. It can also read that internally stored date. When subsequently executing ALREADY.COM on the same day, it finds the DOS date and its internally stored date are the same. Each time it executes, ALREADY issues an exit code acknowledging if it found a new date or not. As an alternative to ALREADY.COM, users with DOS 3.3 or later can use a simple batch file procedure to accomplish the same task. It relies on finding a previously created zero byte file whose name incorporates the date. There is one small problem with both of these once-a-day solutions. They work on the first warm or cold boot after midnight. That could be a problem for anyone who habitually works late at night. Those users deserve the option of receiving the full treatment when they arrive after a full night's sleep. One work-around is to set the DOS clock late. Then, by the time the DOS clock hits midnight, the real time is several hours after midnight. Late workers who typically work until 6am will want to run their clocks at least six or seven hours slow. That is the simplest way to do it. It will work in a pinch, but I find it irritating to have my clock mis-set this way. I will present a modified once-a-day batch file procedure to work around this problem. It prevents once-a-day operations from running until after some set time each day. Once-a-day operations only run after the date has changed and after some specific time determined by the user. The one little "gotcha" is that the date and time formats must be reset to get this one to work. American users who want to use this procedure will have to get used to a dd-mm-yyyy date format, instead of their usual mm-dd-yyyy format. A slightly different, but related, problem sometimes occurs. There are users who like to manually re-execute AUTOEXEC.BAT after boot-up is complete. Their aim is to reset the DOS PROMPT or other system parameters. Unfortunately, there are items in most AUTOEXEC.BAT files that should not execute except at boot-up. Among the obvious items to avoid are reloading TSRs, reinstalling a disk cache, reinstalling RAM disks, reinstalling the network drivers, etc. I call this the once-a-session dilemma. A similar problem arises for anyone who wants to copy AUTOEXEC.BAT to a RAM disk to speed it up. The commands that copy the file to the RAM disk must not re-execute on the RAM disk. There are a couple of ways around this once-a-session dilemma, too. I will present a single batch file procedure that handles both versions of this once-a-session dilemma. ONCE A DAY - ALREADY.COM LISTING 1 includes the complete script for ALREADY.COM. Ed Quillen wrote ALREADY.COM and it appeared on page 260 in the October 1991 edition of PC/Computing. If the DOS date and the internally stored date are not the same, ALREADY.COM creates a new copy of itself that includes the current DOS date and issues an exit code of zero. If the DOS date and the internally stored date are the same, ALREADY.COM exits and issues an exit code of one. =========================================================================== LISTING 1 - ALREADY.SCR n already.com e 0100 b4 2a cd 21 3b 16 37 01 e 0108 75 0b 3b 0e 39 01 75 05 e 0110 b8 01 4c cd 21 89 16 37 e 0118 01 89 0e 39 01 ba 3b 01 e 0120 33 c9 b4 3c cd 21 8b d8 e 0128 ba 00 01 b9 47 00 b4 40 e 0130 cd 21 b8 00 4c cd 21 00 e 0138 00 00 00 41 4c 52 45 41 e 0140 44 59 2e 43 4f 4d 00 rcx 0047 w q =========================================================================== ALREADY.SCR must have a blank line after the final Q. There are no capital O's in the ALREADY.SCR script. Those are all zeros. Store ALREADY.SCR in the TESTUTIL directory. To create ALREADY.COM from the script file, switch to the TESTUTIL directory and issue the following command. DEBUG dateit.bat CALL dateit DEL dateit.bat IF EXIST %DATE%.DT GOTO skip DEL *.DT REM >%DATE%.DT \ Things to do once a day go here; as many lines as necessary / :skip SET DATE= \ Everything from this point executes every time AUTOEXEC.BAT executes. / =========================================================================== This is a slick little procedure and well worth studying. The trickiest part is using DOS redirection in REM statements. REM means remark and some batch file programmers have the impression that DOS ignores these remarks. Not true! DOS reads the whole line following a REM command and creates the directory entries for the file(s) it will need in order to respond to all the DOS redirection command(s). It does not create the file, just the directory entry for each file. It creates what is usually called a zero-byte file. Chris DeVoney used an ECHO Y in place of the REM, but REM is cleaner. Either one will work, however. Either the command REM |MORE |DATE >DATEIT.BAT as shown in LISTING 3, or the command ECHO Y |MORE |DATE >DATEIT.BAT will redirect the output of the DOS DATE command into a new batch file called DATEIT.BAT. Do not forget to include the MORE filter. Without it the PC will hang waiting for a carriage return. Ordinarily, the DATE command stops and asks for user confirmation or a new date. The pipes from REM (or ECHO Y) and MORE prevent the DATE command from stopping. After the REM command finishes creating the file, DATEIT.BAT contains two lines, something like the following. Current date is SUN 12-29-1991 Enter new date (mm-dd-yy): The actual date will vary, of course. The next line in AUTOEXEC.BAT calls DATEIT.BAT as a subroutine. When COMMAND.COM reads the first line in DATEIT.BAT, it looks for a DOSKEY macro named CURRENT. Next it checks to see if CURRENT is an internal DOS command. Then, it checks for files named CURRENT.COM, and CURRENT.EXE. If none of those other commands and files exist in the current directory, COMMAND.COM looks for and finds CURRENT.BAT. As it turns out, this is just a simple batch file jump. The jump to CURRENT.BAT does not trigger a subroutine return to AUTOEXEC.BAT. COMMAND.COM never reads the second line of DATEIT.BAT. The unconditional jump prevents that from happening. CURRENT.BAT executes and sets the environment variable, DATE, equal to the replaceable parameter, %4. The fourth command line parameter after the word CURRENT in DATEIT.BAT is 12-29-1991. Therefore, DOS interprets the second command in CURRENT.BAT as follows. SET DATE=12-29-1991 Once the SET command executes, CURRENT.BAT ends. That triggers a subroutine return to AUTOEXEC.BAT. Execution picks up at the line following the CALL command. There is no further use for DATEIT.BAT and this scheme deletes it. The next command looks for a file named 12-29-19.DT. If it already exists that means the once-a-day commands were run earlier. In response, the execution pointer jumps past the lines that execute once-a-day. Note that the date is truncated to include only the first 8 characters of the date in the file name. Note, too, that if the computer happens to go unused, or possibly un-booted, for a whole year and boots up on the same date one year later, it will not execute the once-a-day lines. That seems a fairly remote possibility. Finally, if DOS is using the date format yyyy-mm-dd, the effect of truncation is that the once-a-day commands execute only once a month. If the file with today's date does not already exist, the first thing this scheme does is delete all the files with the DT extension. That keeps the *.DT files from accumulating. They don't take up much space, just the 32 bytes in a directory entry, but why waste any space at all? Next, it creates a new file with a name that incorporates the new date and the DT extension. A second REM command is used for this purpose. Chris DeVoney used an ECHO Y, again. Both the command REM >%DATE%.DT as shown in LISTING 3 and the command ECHO Y >%DATE%.DT can create the file. Using REM is preferred because it creates a zero byte file that only uses the space occupied by one directory entry. Using the ECHO command consumes a directory entry and also uses one full cluster, usually 2K on a hard disk. Both the disk cluster and the time used to write data into that cluster are wasted when using the ECHO command instead of REM. After it creates that file, the batch file runs the once-a-day procedures. The only other clean-up is erasing the useless environment variable. It is not obvious at this point, but this scheme relies on the fact that the standard date format uses minus signs (-) between the numbers. Anyone working with a country date format that uses slashes (/) or periods (.) between the numbers will find that this procedure does not work in DOS 5.0. They should see the discussion below on changing the country format. The alternative is to write another EDLIN script file, like FIXER.TXT (described below), to replace the slashes or periods with minus signs before attempting to create the date specific file. ONCE A DAY - THE MODIFIED BATCH FILE APPROACH To use this scheme, you'll need four files instead of the two used in the simple batch file approach. You'll need a revised version of CURRENT.BAT. Again, copy it to JUNK.DT. You'll also need a copy of Ronny Richarson's program, SETERROR.COM. Finally, you'll need to create an EDLIN script file called FIXER.TXT. The new version of CURRENT.BAT appears at the top of LISTING 4. Two files, DATEIT.BAT and TIMEIT.BAT, will each call CURRENT.BAT in this modified scheme. Each includes a jump to CURRENT.BAT, but each needs it to respond differently. A short procedure for manually creating FIXER.TXT using EDLIN appears in the middle of LISTING 4. Recreate the procedure so it appears on your screen exactly as it does in LISTING 4. Use the F6 key to create the ^Z wherever it appears in the procedure. FIXER.TXT contains the script that tells EDLIN how to modify TIMEIT.BAT. The script tells EDLIN to replace the colons in the time with spaces. SETERROR.COM is only available, as far as I know, by buying one of Ronny Richardson's books. These are "MS-DOS Batch File Programming - 2nd Edition" and "MS-DOS Batch File Utilities." You'll also have to modify your CONFIG.SYS file to include the COUNTRY command as shown in LISTING 4. If COUNTRY.SYS is located in a different directory, use that directory name in this command. For American users no other changes are required. Finally, at the bottom of LISTING 4, is the scheme that implements this new procedure. The plan is to prevent once-a-day operations from occurring until the first reboot after some specific hour of the day. The time can only be specified to the nearest hour. Change the number in the IF NOT ERRORLEVEL test if 7am is not a good time for you. The COUNTRY command in CONFIG.SYS puts the PC on a 24 hour time format, but keeps the standard American Code Page. Any hour from 1 to 23 will work. Whatever hour you choose, remember that the once-a-day operations only execute if you boot between the selected time and midnight. Don't make it too late in the day. =========================================================================== LISTING 4 - Revised CURRENT.BAT and the Modified Scheme for using it. Contents of the modified CURRENT.BAT @ECHO off IF "%1"=="time" SET time=%3 IF "%1"=="date" SET date=%4 Procedure for Creating FIXER.TXT with EDLIN C> edlin fixer.txt New file *i 1:*1,1r:^Z 2:*2d 3:*#i 4:* 5:*Type anything here 6:*^Z *5 5:*Type anything here 5:*^Z *#i 6:*e 7:*^Z *e C> Note: FIXER.TXT has an extra space at the end of line 1. Hit the space bat one time just before hitting the {RETURN} key on line 1. The command on line 1 replaces every colon with a space character in the first line of the TIMEIT.BAT file in the scheme below. You cannot reedit FIXER.TXT with a normal ASCII text editor, not even with EDLIN. If you make a mistake, delete the file and start over. When I prepared this manual script for creating FIXER.TXT, I included everything that either EDLIN or the user types on the screen. Enter only the characters that EDLIN does not fill in for you. New Line to Add to CONFIG.SYS COUNTRY=031,437,C:\DOS\COUNTRY.SYS Schematic of AUTOEXEC.BAT for Modified Once-a-Day Operations @ECHO off REM Start of AUTOEXEC.BAT \ Opening commands always execute / C: CD\already REM |MORE |TIME >timeit.bat C:\DOS\EDLIN timeit.bat nul SET time= IF NOT ERRORLEVEL 7 GOTO skip REM |MORE |DATE >dateit.bat CALL dateit DEL dateit.bat IF EXIST %DATE%.DT GOTO skip DEL *.DT REM >%DATE%.DT \ Things to do once a day go here; as many lines as necessary / :skip SET DATE= \ Everything from this point executes every time AUTOEXEC.BAT executes. / Note: This scheme assumes that EDLIN resides in the C:\DOS directory. It also assumes that SETERROR.COM and FIXER.TXT reside in the same directory as CURRENT.BAT. =========================================================================== AUTOEXEC.BAT ON A RAM DISK The once-a-session dilemma really has two parts. I'm going to combine them in one package. Here I present the case where AUTOEXEC.BAT copies itself to a RAM disk to speed operations at boot-up. After the copy of the RAM disk completes its tasks, AUTOEXEC.BAT on the hard disk takes over and erases the copy from the RAM disk. A user who subsequently tries to execute AUTOEXEC.BAT manually will only be able to execute the commands which the programmer authorizes for that purpose. The following is based in part on a tip reported by Robert Cutler on page 411 of the June 11, 1991 edition of PC Magazine. The idea is to create a RAM disk and copy AUTOEXEC.BAT there. The AUTOEXEC.BAT in the root directory of the C: drive should copy and then CALL the copy of itself stored in the root directory of the RAM drive. It is inefficient to use two different files for this purpose; storing an unnecessary file on the hard disk wastes at least one cluster. The question is, therefore, how to prevent the copy of AUTOEXEC.BAT on the RAM drive from getting into a loop where is copies itself to the RAM drive and then jumps to the first line of the new copy endlessly. Robert Cutler's discovery (at least, it's new to me) is that the %0 parameter for AUTOEXEC.BAT is null when it executes at boot-up. During boot-up, the copy of AUTOEXEC.BAT installs the TSRs. During any subsequent manual execution of AUTOEXEC.BAT, it should processes only the innocuous operations. These might include resetting the PATH and PROMPT variables, the screen colors, the keyboard speed, the modem parameters, etc to their initial values. There are other techniques that might be used to do this, of course. ISDEV.COM is one possibility. It might be used to test for the existence of a RAM drive before AUTOEXEC.BAT installs it. ISDEV cannot detect most TSRs. RAM drives installed by AUTOEXEC.BAT are the only exception that I know of. Another possible method, recommended by Malcolm McLean on page 334 of the April 10, 1990 edition of PC Magazine, is to execute AUTOEXEC.BAT with a command line parameter when CALLing the copy on the RAM disk. I like that idea. I think of the command line parameter as a password. On page 249 of the March 1991 edition of PC World, Bill Weil described a method for once a session operations based on using environment variables. This is inefficient because his scheme consumes valuable environment space protecting the user from what has to be a rare problem. What I prefer is a combination of techniques. That combination makes it hard, though not impossible, to execute the dangerous parts of AUTOEXEC.BAT from the command line. All I'm trying to do is prevent inadvertent actions by naive users. The scheme for AUTOEXEC.BAT appears in LISTING 5. This example assumes that CONFIG.SYS installs the RAM disk and that it is drive G:. Because it uses the CALL command, this scheme only works with DOS 3.3 and above. =========================================================================== LISTING 5 - AUTOEXEC.BAT that installs TSRs only from the RAM disk. @ECHO off IF NOT "%0"=="" GOTO again REM >G:\quit.bat COPY autoexec.bat G:refresh.bat G: REM Pick any password you like, and use it as a parameter. CALL refresh z1Q9aXX0t DEL G:\refresh.bat G:\quit :again REM Use the same password listed above, REM the test is case sensitive. IF NOT "%1"=="z1Q9aXX0t" GOTO no_tsr IF NOT "%0"=="REFRESH" GOTO no_tsr IF NOT EXIST G:\refresh.bat GOTO no_tsr \ Load TSRs and run other once-per-session programs/commands here / :no_tsr \ Include the scheme for once-a-day operations here / :skip \ Include repeatable programs/commands here / =========================================================================== The logic is clear but perhaps it is little too tight to easily grasp at first glance. We'll trace the path of the execution pointers during the boot-up sequence to see how all this works. Initially, AUTOEXEC.BAT exists only in the root directory of the C: drive. At boot-up, the first thing it does is check the value of the replaceable parameter, %0. Ordinarily, %0 holds the name of the batch file, itself. In the special case of AUTOEXEC.BAT executing at boot-up, however, DOS does not put the name into %0. If %0 is null, it is certain that this is the AUTOEXEC.BAT file running at boot-up. If this is a boot operation, then AUTOEXEC.BAT also creates a zero byte file, called QUIT.BAT, using the REM command. QUIT.BAT provides a shortcut escape route from AUTOEXEC.BAT on the hard disk. Some might want to use ISDEV to make sure that G: exists. Then AUTOEXEC.BAT copies itself under the name REFRESH.BAT to the RAM disk. AUTOEXEC.BAT makes the RAM disk the current drive and CALLs REFRESH.BAT as a subroutine with a parameter that it uses like a password. Remember that REFRESH.BAT contains exactly the same commands as AUTOEXEC.BAT. It, too, starts out by checking the value of %0. This time %0 is not null and the execution pointer moves to the label :again. Before actually loading any TSRs, REFRESH.BAT checks three items. First, it checks for the correct password. Next, it checks to see if the current file's name is REFRESH. Finally, it checks to see if G:\REFRESH.BAT exists. If any of those tests fail, it skips the entire TSR loading section. The effect of these three tests is to prevent TSR loading unless the command line includes the correct password, the current file's name is REFRESH.BAT and REFRESH.BAT exists on the RAM disk. Re-executing AUTOEXEC.BAT with or without a password will only execute the safe commands later in the file. Even if the user knows enough to rename AUTOEXEC.BAT to REFRESH.BAT, the TSR section still won't run, with or without the password. The final impediment is that it will be necessary to create a file named REFRESH.BAT on the RAM disk before a user could force the TSR loading sequence to run a second time. This would hardly be considered an inadvertent action. After passing these tests, REFRESH.BAT goes on to load the TSRs and runs all the rest of the start-up operations. It can even include the once-a-say scheme described earlier. When REFRESH.BAT finishes, the subroutine return pops the execution pointer for AUTOEXEC.BAT on the C: drive. (The execution pointer in the calling batch file is PUSHed into the return stack and saved while the subroutine executes. After the return, the old execution pointer is POPped from the stack. The subroutine stack uses a last-in/first-out scheme for finding the next execution pointer to POP from the stack.) Execution in AUTOEXEC.BAT picks up on the line following the CALL to REFRESH.BAT. The first order of business is to delete REFRESH.BAT. The RAM disk is empty (except for QUIT.BAT and it only uses a directory entry) and ready for other uses. Finally, AUTOEXEC.BAT executes a jump to QUIT.BAT. That is the end of AUTOEXEC.BAT. Instead of jumping directly to QUIT.BAT, you could include a jump or a call to a batch file that runs your menu system, for example. You could execute an application program instead of a batch file. Do not remove the jump to QUIT.BAT. Put the batch file or application program command between the DEL G:REFRESH.BAT command and the jump to QUIT.BAT. KEY WORDS ALREADY.COM ALREADY.SCR AUTOEXEC.BAT Batch File Batch File Jump Batch File Subroutine CALL Cluster Code Page Cold Boot Command Line Parameter COMMAND.COM CONFIG.SYS COPY COUNTRY COUNTRY.SYS CURRENT.BAT DATE Date Format dd-mm-yyyy Debug DEBUG DEL Directory Entry Disk Cache DOS 3.3 DOS 5.0 DOS Clock DOS Date DOS Prompt DOS Redirection ECHO EDLIN Environment Variable Execution Pointer Exit Code E-Mail F6 Key File Extension FIXER.TXT Hardware Diagnostic IF NOT ERRORLEVEL Test ISDEV.COM mm-dd-yyyy MORE MS-DOS Batch File Programming - 2nd Edition, by Ronny Richardson MS-DOS Batch File Utilities by Ronny Richardson Once-a-Day Dilemma Once-a-Session Dilemma Password PATH PC Magazine - April 10, 1990 PC Magazine - June 11, 1991 PC World - March 1991 PC/Computing - June 1991 PC/Computing - October 1991 Pipes Piping POP PROMPT PUSH RAM Disk RAM Drive RAMDRIVE.SYS Reboot REM Replaceable Parameter Return Script SETERROR.COM System Parameter TIME Time Format TSR Unconditional Jump Utility Warm Boot Working Directory yyyy-mm-dd Zero Byte File ^Z - Control Z {RETURN} Key