Creating Secondary Batch Files and Scripts


Batch files often rely on secondary batch files and debug scripts in order to complete their tasks. Often, these files can't be allowed or be trusted to exist on a permanent basis. They must be generated and deleted "on-the-fly", as they are needed.

The most common technique for generating secondary files is through the use of the echo command:
 
MAIN.BAT TEMP.BAT
@echo off
echo Main running
echo @echo off > temp.bat
echo echo Temp running >> temp.bat
call temp.bat
del temp.bat
echo Main ending
<<< This batch file

will generate this >>>

@echo off
echo Temp running
Frankly, this is simple enough that it doesn't bear explaining much beyond the example code. Unless you'll be needing to put special characters in the secondary file, this technique will probably be all you need.


A problem occurs when you want the secondary TEMP.BAT to contain redirection or piping characters. Suppose you want TEMP.BAT to contain the following line:
chkdsk | find "65535" > nul
(By the way, the above command will usually return an errorlevel of one if you have a boot-sector virus or are using special boot software)
This line, for example, won't work:
echo chkdsk|find "65535">nul > TEMP.BAT
What happens is DOS simply processes things left to right, sending the string "chkdsk" into FIND, which fails to find "65535", which dumps nothing into nul (and sets an errorlevel of one), then sends nothing into TEMP.BAT, resulting in a zero-byte TEMP.BAT file. Not good. If we put quotes around the whole thing, it all gets into TEMP.BAT, but with the quotes still intact:
echo "chkdsk|find "65535">nul" > TEMP.BAT
puts
"chkdsk|find "65535">nul"
into TEMP.BAT. Clearly we're making progress, but we aren't there yet. To go the next step requires an understanding of how DOS processes lines (A little something Dale Edgett pointed out for all of us in PC Magazine):

DOS parses a line once, from left to right. If it finds a double quote ( " ), it treats everything up to the next double quote as text. If it finds %%, it replaces them with %. If it finds % followed by a number (%0 through %9), it replaces those two characters with the value of the corresponding command line argument. If it finds % followed by anything else, it replaces everything up to the next % with the value of the corresponding environment variable (for example, %PROMPT% would probably get replaced by $P$G).

Now, let's suppose we had this line in our TEMP.BAT:
%"% chkdsk | find "65535" > nul %"%
What would happen is the %"% characters would be replaced by the value of the environment variable whose name is ". Well, as long as you don't have a variable whose name is a double quote, this means they will be replaced by nothing. In other words, it is totally harmless to stick %"% in anywhere we want.

So what would happen if our MAIN.BAT had the following line in it?
echo %%"%% chkdsk | find "65535" > nul %%"%% > temp.bat
DOS would replace the first %% with a single %, then it would see the quote. Seeing the quote, it would remember to ignore all piping and redirection symbols up to the next quote. This means our quote protection ends at the quoted 65535, but it picks up again at the end of the number and continues to the last quote (far enough to protect all but the last redirection symbol which we want to act as a redirection anyway). DOS then continues to process the line, replacing the rest of the %% with %. After processing the line, it executes it, echoing our desired line
%"% chkdsk | find "65535" > nul %"%
into TEMP.BAT. All the piping and redirection is delivered intact, and we can ignore the %"%, since DOS will ignore it.


Probably the easiest way to echo complex lines into a secondary batch file is to make them unique, then TYPE the entire main batch file through FIND, letting FIND filter things so that only the desired lines get through. The trick is to find something unique you can add to the desired lines that won't affect what those lines do. The universal answer is spaces. Whether you will be creating a DEBUG script, a QBASIC file, or a batch file, leading spaces are always ignored. Consider this file named MAIN.BAT:

goto process
   chkdsk | find "65535" > nul
   if errorlevel 1 echo You have a virus!
:process
type MAIN.BAT | find "   "| find /v "    " > TEMP.BAT
call temp.bat
del temp.bat

Because the two lines we want sent to TEMP.BAT are preceeded by three spaces, they are easy to pick out of the batch file. Unfortunately, the line with the FIND in it also has three spaces in it (between the quotes). By adding a reverse search for another unique string (I chose four spaces), we can insure that the FIND line won't find itself!.Only the desired lines will be redirected into the TEMP.BAT. If we wanted to, we could add more code to create another temporary batch file. The lines for this next file would have four spaces at the beginning of each line, and the FIND line for it would do a reverse search on five spaces. Another batch file could be created that would be identified by five leading spaces, and it's FIND line would include a reverse search for six spaces. And so on. Very flexible. And all that indenting makes the code easier to read too!

The idea of using reverse searches for spaces is only needed if you will be creating more than one secondary batch file. If you are only spinning off a singel file, your reverse search can be for any unique string:

type MAIN.BAT | find "   "| find /v "BUT NOT THIS LINE!" > TEMP.BAT


A hybrid approach which contains the best (the worst?) of the two above techniques eliminates the need for a reverse search when creating secondary batch files (not DEBUG or BASIC files). Relying on the fact that nonexistent environment variables can be inserted in a batch file without harm, we can use them as markers to make desired lines unique. Consider this MAIN.BAT example:

goto process
%"1% chkdsk | find "65535" > nul
%"1% if errorlevel 1 echo You have a virus!
:process
type MAIN.BAT | find "%%""1%%" > TEMP.BAT
call temp.bat
del temp.bat

Trust me for just a second. Because we use TYPE and FIND, our desired lines (the second and third lines, both marked with %"1%) will be sent exactly as they appear into the secondary TEMP.BAT. These two lines won't be "processed" until TEMP.BAT is run. When that happens, %"1% will be replaced by the value of the environment variable "1 , which is nothing. That much was easy to figure out. Now I'll earn your trust and explain the FIND line... First, DOS will process that line and convert the %% to %, reducing our FIND expression from
find "%%""1%%"
to
find "%""1%"
Next, we have to concern ourselves with what FIND will be looking for, namely everything inside the quotes:
%""1%
But -- did you know how to make FIND search for a quote ( " )? You put two of them in a row ( "" ). So what FIND will actually be searching for is
%"1%
Since that particular string only occurs on the second and third line of MAIN.BAT (our desired lines), they are the only ones that will be found by FIND. No reverse search is needed thanks to the way FIND translates quotes!

If you need to have your MAIN.BAT create several secondary batch files, you could label and find them this easily:
 
Unique Line Markers
FIND
%"1% find "%%""1%%"
%"2% find "%%""2%%"
%"3% find "%%""3%%"
%"4% find "%%""4%%"
%"batchfile5% find "%%""batchfile5%%"
The important thing is that you keep the quote. Whether you add numbers or names after the quote is up to you.


The old way of getting redirection and pipe symbols into secondary batch files was to capture a prompt. The advantage of this way is that you don't have to know the name of the file, since you won't be "typing" it. And it really only takes two lines of code. Unfortunately, it does run a separate DOS session (do you have enough memory?). Consider this example:

echo @prompt chkdsk $B find "65535" $G nul > temp1.bat
command /c temp1.bat > temp2.bat
echo if errorlevel 1 echo You have a virus!>> temp2.bat
call temp2.bat

When the first line in the above batch file is run, it will create a secondary batch file TEMP1.BAT containing:
@prompt chkdsk $B find "65535" $G nul

When this TEMP1.BAT is run by COMMAND (allowing everything, including the prompts, to be redirected and captured), it will set the prompt to
chkdsk | find "65535" > nul
because $B becomes a | and $G becomes > and we capture the prompt just like that and redirect it into TEMP2.BAT. Since the second line of my desired TEMP2.BAT has no special characters, I simply echo it and append it to TEMP2.BAT.  TEMP2.BAT will then contain
chkdsk | find "65535" > nul
if errorlevel 1 echo You have a virus!


There is another way of getting pipes and redirections into secondary batch files by capturing a prompt. It doesn't launch a separate DOS session and it doesn't create files that have extra spurious carriage returns in them, but it only works under Windows 95 (At least I know it won't work under DOS 6.00 or 6.22).

@ctty nul
prompt chkdsk $B find "65535" $G
echo on
if exist nul>temp.bat
nul
echo off
prompt $p$g
ctty con
echo if errorlevel 1 echo You have a virus!>> temp.bat
call temp.bat

This is a totally undocumented trick (bug, feature?). If you run IF EXIST (or IF NOT EXIST) without the required command and specify a redirection destination instead, and if ECHO is ON, and if the IF test is true, then the command following the IF line will be redirected, prompt and all into the file specified on the IF line.

First we set the prompt to
chkdsk | find "65535" >
We can't put the nul on the end of the prompt, because we need a non-blank line following the IF line (and that's where the nul will go). Since we need the IF line to test true, we look for nul. NUL is a device which always exists, so IF EXIST NUL will always be true. The next line is also nul (a coincidence), and this line will be (1) run as a command, and (2) be captured appended to the prompt. Luckily, nul is not a valid command, so nothing bad will come of it being on a command line (except a harmless error message, which is what the ctty is hiding). At this point, we will have
chkdsk | find "65535" >nul
in TEMP.BAT. We can then undo all our setup work, fixing echo, prompt, and ctty. The final step is to add the second line to our TEMP.BAT, but this is a no-tricks simple redirection.
 
 




 






Lost? Look at the site map.

Bad links? Questions? Send me mail.

Google
Yahoo
Ask Jeeves