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 |
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 |
|
%"1% | find "%%""1%%" |
%"2% | find "%%""2%%" |
%"3% | find "%%""3%%" |
%"4% | find "%%""4%%" |
%"batchfile5% | find "%%""batchfile5%%" |
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.