Eric's WebspaceNOTES ON DOS BATCH PROCESSING

YEW KWANG HOOI

UNIVERSITI TEKNOLOGI PETRONAS

Sunday, October 05, 2003

SOURCE:

http://www.ericphelps.com/batch/

http://www.microsoft.com/technet/treeview/default.asp?url=/technet/prodtechnol/winxppro/proddocs/ntcmds_shelloverview.asp

 

 

 

T.O.C:

 

Batch File ReadMe. 2

Getting User Input 4

Creating Secondary Batch Files and Scripts. 13

Batch File Programming: Stupid Useless Tricks. 18

Command shell overview for Windows XP. 26

Links. 83

 

 


Batch File ReadMe

 

Where to find the missing commands: If you have access to your Windows 95 CDROM, you can find almost everything in the \Other\Oldmsdos directory. If you're like me, you'll copy every bit of it into your \Windows\Command directory. If you don't have access to your CDROM, check out Microsoft's web site. The actual files (1.47 megabytes compressed) can be found at:
ftp://ftp.microsoft.com/Products/Windows/Windows95/CDRomExtras/OtherUtilities/olddos.exe
ftp://ftp.microsoft.com/softlib/mslfiles/olddos.exe
Other useful DOS commands are winset.exe and shortcut.exe. Winset lets you set environment variables globally (they persist once your batch file ends). Shortcut lets you make and modify shortcuts from the command line. You'll find both of these together as "envars.exe" on your CDROM under \Admin\Apptools\Envvars or from Microsoft at:
ftp://ftp.microsoft.com/Products/Windows/Windows95/CDRomExtras/AdministrationTools/ApplicationTools/envars.exe

Where to get help on DOS commands: You should already know about the /? option that every DOS command responds to. If you have Windows 95 you may wonder why you can't find DOS command help by using the Help icon on the Start menu. I wonder. But if you get the missing "OldDos" commands (see the item above), you'll get the HELP command. Actually, you don't need the HELP.COM file. All it does is run QBASIC with the undocumented /QHELP option (uppercase) which opens up the HELP.HLP data file. Yes, you get QBASIC.EXE and HELP.HLP with the OldDos stuff. You'll also find useful info (even if you thought you knew it all) by reading the text files in your Windows directory. If you installed Windows in your C:\Windows directory, I can save you looking for some of them by clicking these links (they point to your local drive) Config.txt, Msdosdrv.txt, Programs.txt, and Tips.txt. I've been told that on German computers, the spelling of some of the file names is slightly different (For example "programm.txt" instead of "programs.txt"), so look around if you don't have an English installation of Windows!

What are those |<>@ symbols for?
Use the "pipe" character "|" (the vertical bar) to send the output from a command into the input of another command.

For example:
type test.txt | program.exe
That would send the output of the "type" command into the input of the "program.exe" command. The "type" command in this case would be putting out the contents of the file "test.txt". The "program.exe" would (in theory) accept that as it's input instead of accepting input from the keyboard. Use redirection characters ">" and "<" to send output between files and programs. Notice the difference? The pipe sends stuff between two PROGRAMS. Redirection is between a program and a FILE. The redirection arrow lets you know what direction the data is flowing. For example:
program.exe > test.txt
would take the output of "program.exe" and put it in the file "test.txt"
INSTEAD of displaying it on the screen. The data flows out of the program "program.exe" and into the file "test.txt". On the other hand:
program.exe < test.txt
Would cause "program.exe" to use "test.txt" as it's input
INSTEAD of taking input from the keyboard. The data flows out of the file "test.txt" into the program "program.exe". So these two lines are different ways of doing the same thing:
type test.txt | program.exe
program.exe < test.txt

The both end up telling "program.exe" to use the data in "test.txt" for input instead of using the keyboard. The difference between ">" and ">>" is that ">"
normally creates a new file, replacing what was there, while ">>" just adds to the end of the file (If the file doesn't already exist, it will be created). You can even use redirection in non-intuitive order and it still works. For example, these two lines do the same thing:
program.exe > test.txt
> test.txt program.exe

Why do it the second way? Sometimes the second way looks neater when you have lots of  program commands going into a single file.
The "@" symbol can be put on the beginning of any command to stop the command from appearing on the screen. Any output from the program goes to the screen, but the command itself doesn't. For example, on a "dir" command, I only want to see a list of files. I do NOT want to see the command "dir".  Normally, you can turn off all screen echoes by using the "echo off" command. So anything after the "echo off" command only shows program output. Unfortunately, the echo command still gets echoed! However, if you put an @ sign in front of the echo command, it turns off the echo from the echo command. That's why most batch files start with this:
@echo off

How do I write batch programs: Geez, do I have to answer this? But people ask... You'd be surprised how many people are in that narrow transition period of knowing how to type commands, but not knowing how to put them together in a batch file. Here is the short version: Get yourself a DOS prompt. Type in the commands you need to do whatever it is you need to do. If your commands work, open up Notepad and type those SAME COMMANDS in the SAME ORDER. Don't type what appeared on the screen, just type what you actually typed in. Save that file with a bat extension ( For example "test.bat"). Now instead of having to type the commands, you can just double-click the batch file. Sure, your first batch file may only have two or three commands, but it counts. It's a batch program.


Getting User Input

There are six main ways of getting user input. Not all work on all systems! Windows Scripting should work on Win98 and newer, and on Win95 if scripting is installed. QBASIC works on everything if it's installed, but nobody ever installs it. The Batch Menu works everywhere, but is exceedingly lame. CHOICE works on all the older machines, but not on NT or newer unless it gets installed. FC/DATE is strictly a Win9x solution, but could work with modifications on newer machines. COPY CON is strictly DOS 6 and older, but can be made to run on Win9x if you install ANSI.

 

Windows Scripting  If you have Win98, NTSP4, or something newer, you should have Windows Scripting. If you don't have it, download it. Here I show a batch file that runs a pre-built visual basic script "userin.vbs". That script, when run, will create a simple one-line batch file "~userin.bat" which puts the user input into the environment. The bad news is that I'm willing to teach batch, but not Windows Scripting. I do have a separate scripting web page, but all you'll get on this page is the sample script with no explanation...

Here is the batch file:

start /w wscript.exe userin.vbs
call ~userin.bat
del ~userin.bat
echo You entered %USERIN%

Now the script file I call "userin.vbs"

strUserIn = InputBox("Enter Data")
Set fs = CreateObject("Scripting.FileSystemObject")
strFileName = fs.BuildPath(Wscript.ScriptFullName & "\..", "~userin.bat")
strFileName = fs.GetAbsolutePathName(strFileName)
Set ts = fs.OpenTextFile(strFileName, 2, True)
ts.WriteLine "set userin=" & strUserIn
ts.Close

While the "two separate files" version above will run on any machine (so far) that has scripting, a single file will act different under NT and 9x. That's because the ampersand has special meaning under NT. When the batch file tries to "echo" a "&" character, NT requires that a caret "^" precede it. If you want to write a batch file that works under NT and 9x, you need to test for how ampersands are handled:

@echo off
:: First test to see if we are on NT or similar OS
:: The ony difference is how they handle the ampersand
 > ~userin.vbs echo 1234&rem
type ~userin.vbs | find "rem" > nul
if errorlevel 1 goto WINNT
goto WIN9X

:WIN9X
 > ~userin.vbs echo strUserIn = InputBox("Enter Data")
>> ~userin.vbs echo Set fs = CreateObject("Scripting.FileSystemObject")
>> ~userin.vbs echo strFileName = fs.BuildPath(Wscript.ScriptFullName & "\..", "~userin.bat")
>> ~userin.vbs echo strFileName = fs.GetAbsolutePathName(strFileName)
>> ~userin.vbs echo Set ts = fs.OpenTextFile(strFileName, 2, True)
>> ~userin.vbs echo ts.WriteLine "set userin=" & strUserIn
>> ~userin.vbs echo ts.Close
goto RUN

:WINNT
 > ~userin.vbs echo strUserIn = InputBox("Enter Data")
>> ~userin.vbs echo Set fs = CreateObject("Scripting.FileSystemObject")
>> ~userin.vbs echo strFileName = fs.BuildPath(Wscript.ScriptFullName ^& "\..", "~userin.bat")
>> ~userin.vbs echo strFileName = fs.GetAbsolutePathName(strFileName)
>> ~userin.vbs echo Set ts = fs.OpenTextFile(strFileName, 2, True)
>> ~userin.vbs echo ts.WriteLine "set userin=" ^& strUserIn
>> ~userin.vbs echo ts.Close
goto RUN

:RUN
:: Now run the created script
start /w wscript.exe ~userin.vbs
del ~userin.vbs

:: Now call the created batch file
call ~userin.bat
del ~userin.bat

:: Now display the data!
echo You entered %USERIN%
pause
cls

If you find yourself needing to enter passwords and you want to mask the passwords so they aren't visible, you can use a combination of scripting and HTML or (if you have IE5 or newer) you can use scripted HTA files.
 

 


QBASIC.  If you aren't using Win9x and want to stick with a non-gui interface, this is it. QBASIC isn't installed on most systems, so you may have to force a download or put the qbasic program files in the same location as your batch file (real easy if it's a network drive!). Again (just like above), I'll teach batch, but not QBASIC. So I'll show a batch file creating a qbasic script, but I won't explain the script. I can only do so much!

The batch file here will generate a separate qbasic script "~usrin.bas", then run that script with qbasic. All the script does is create a separate batch file "~usrin.bat" that puts the user input into the environment.

@echo off
echo OPEN "~usrin.bat" FOR OUTPUT AS #1> ~usrin.bas
echo INPUT "Enter your name ", sUsrin$>> ~usrin.bas
echo PRINT #1, "set usrin="; sUsrin$>> ~usrin.bas
echo CLOSE #1>> ~usrin.bas
echo SYSTEM>> ~usrin.bas
qbasic /run ~usrin.bas
call ~usrin.bat
del ~usrin.bat
del ~usrin.bas
echo Your name is %usrin%
pause
cls

I've had scattered reports that the above code doesn't run on code page 437 (US) but works on code page 850 (Multilingual). But I use code page 437, and the code works identically for me under Win95 and NT4. Just FYI.  Eric Rose, someone who knows NT better than I do, found that if you have problems with QBASIC using expanded memory in ways NT finds unacceptable, you can make everybody happy by adding this line

set RTVMEXP=0

to the beginning of the batch file (actually the second line, just after the "@echo off" line).

In the above example, I create the QBASIC  code as it is needed. You can speed things up considerably by creating the code ahead of time and having it kept as a permanent item. Here is  the code rewritten as two separate files.  First, the batch file:

@echo off
qbasic /run userin.bas
call ~userin.bat
del ~userin.bat
echo Your name is %userin%

Now the QBASIC file I call "userin.bas"

OPEN "~userin.bat" FOR OUTPUT AS #1
INPUT "Enter your name ", sUsrin$
PRINT #1, "set userin="; sUsrin$
CLOSE #1
SYSTEM

If you find yourself needing to enter passwords and you want those passwords masked, you can use QBASIC to mask the passwords with asterisks.



Batch file menus.  Don't laugh. This is the first thing they teach in most DOS books as how to get user input. If you want the user to choose one of three options, you make a simple menu, then make three batch files whose names correspond to the menu choices:
@echo off
echo Please pick a number and hit Enter:
echo 1 - Doom
echo 2 - Duke
echo 3 - Keen
After the above batch file runs, the user is dropped back to a DOS prompt. All you'd have to do is create three more batch files ( 1.bat, 2.bat, and 3.bat ) which would launch the appropriate programs.


CHOICE.  At least you won't have to press Enter. CHOICE responds immediately to your keypress. Unfortunately, the CHOICE command is not generally available under NT. You can get it on the disk version of the Resource Kit, but not on the download version. If you really want to implement this Win9x solution on NT, you can always steal a copy of  CHOICE.EXE from a Win9x box. All reports I've heard say it works just fine.

@echo off
choice /n /c:123 Please choose 1 for Doom, 2 for Duke, or 3 for Keen:
if errorlevel 3 echo Keen
if not errorlevel 3 if errorlevel 2 echo Duke
if not errorlevel 2 if errorlevel 1 echo Doom
In this case, all I did was echo the word corresponding to the number you select. You'd probably want to run a more useful command or launch another batch file. Read the help on IF to learn more about ERRORLEVEL. Because ERRORLEVEL always tests for a "greater than or equal to" condition, complicated testing for exact error levels like shown above (Or using lots of GOTOs and labels) is usually necessary.

If you want to use CHOICE in an somewhat more complicated way, you could have it check for every possible keypress, then accumulate each key in an environment value. This way you could build up an entire word or number a character at a time. The following example shows a limited password-entry example:

@echo off
set userin=
echo Please enter a sequence of letters a, b, or c. Enter q to quit.
:rerun
choice /n /c:abcq > nul
for %%x in (1,2,3,4) do if errorlevel %%x goto add%%x
:add1
set userin=%userin%a
goto rerun
:add2
set userin=%userin%b
goto rerun
:add3
set userin=%userin%c
goto rerun
:add4
echo Your sequence was %userin%

In the above example, I only checked for three legal input characters, but it is easy (though code-intensive) to extend. Also note how I seem to have suspended the rules for ERRORLEVEL testing on the FOR line. It seems that FOR passes the values on to IF in reverse order. Turning echo on shows FOR evaluates in 1234 order, but IF gets it as 4321. After the first true condition, the GOTO stops IF from coming back to evaluate the next condition.


FC and DATE All the batch gurus know this trick. Just like the "copy con" example above, we use the CON device to get user input. But instead of trying to define the end of con by adding a ctrl-z, we switch viewpoints and define when we will quit reading con. The FC command can do this with it's /LB option. The trick is to use FC not to compare files (which is what FC is made for), but to compare the CON device with the NUL device. Treating devices like files comes easy to Unix types, so it'll make you seem more worldly if you act like it doesn't bother you.

Now, the NUL device has nothing in it, so when FC compares NUL to CON, the difference will always be exactly what the user keys into CON. Duh. But FC's /lb option allows us to specify how many different lines will be accepted. With /lb1 specified, FC will quit reading con after the first different line (which will be the first line). All the user has to do is hit "Enter" to define the end of the line.

We also use FC's /n (line numbering) option for two reasons: First, FC puts out quite a few lines. Having it number the lines makes it easy to FIND the line we want. Second, we'll be putting the output of FC into the input of DATE (wonder why?). By numbering the line, we can allow for the otherwise embarrassing problem of having the user enter as a first word something that might be interpreted as a date. The first word will be "1:", which is not a date.

Let me illustrate FC. First, with no options. Notice how I had to enter a Ctrl-z to terminate things. FC's output starts with the line containing "****** CON". My input is the line "this is a test".

C:\Temp>fc con nul
Comparing files CON and nul
this is a test
^Z
****** CON
this is a test
****** nul
******

Next, I'll add in the /LB1 option.

C:\Temp>fc /lb1 con nul
Comparing files CON and nul
this is a test
Resync failed.  Files are too different
****** CON
this is a test
****** nul
******

Notice all I had to do was hit Enter. Extracting the original line from FC's output poses problems. No matter how you configure FIND, a user could enter something which could mess you up. So yes, now I'll demonstrate FC's line numbering:

C:\Temp>fc /lb1 /n con nul
Comparing files CON and nul
this is a test
Resync failed.  Files are too different
****** CON
     1:  this is a test
****** nul
******
 
 
 
C:\Temp>

Now it would be trivial to use FIND to extract the desired line by searching for "1:". But I want you to notice something else. This time I showed the next prompt. See how much room there is between FC's output and the prompt? FC always adds a blank line to it's output. This is going to come in real handy, because next I'll pipe the output of FC into DATE.

C:\Temp>fc con nul /lb1 /n | date
this is a test
Current date is Wed 05-14-1997
Enter new date (mm-dd-yy): Comparing files CON and nul
 
Invalid date
Enter new date (mm-dd-yy): Resync failed.  Files are too different
 
Invalid date
Enter new date (mm-dd-yy): ****** CON
 
Invalid date
Enter new date (mm-dd-yy):      1:  this is a test
 
Invalid date
Enter new date (mm-dd-yy): ****** nul
 
Invalid date
Enter new date (mm-dd-yy): ******
 
Invalid date
Enter new date (mm-dd-yy):
 
C:\Temp>

If you've ever tried to set the date, you've noticed how persistent DATE is. It will keep asking you to enter a new date until you either enter a date or until you just press Enter. Since we never (in this example) give DATE a valid date, it will keep rejecting our lines until it hits the blank line at the end of FC's output. If you count, you'll see my single line has resulted in twenty lines of output from DATE (6 of which are blank). By piping DATE's output through FIND looking for "1:", we'll end up with just one line:

Enter new date (mm-dd-yy):      1:  this is a test

Now, if we were to take that line and call it a batch file (Which I'll call TEMP.BAT), when we ran it it would try to execute the ENTER command (Since "Enter" is the first word on the line). Luckily, there is no "enter" command, so we can write our own batch file called ENTER.BAT. When TEMP.BAT runs, it will run our ENTER.BAT and pass new date (mm-dd-yy): 1: this is a test to ENTER.BAT as arguments. Notice how "this" (The first word I typed) is the fifth argument ("new" is first, "date" is second, etc.).

Now it's time to show the completed example. This only asks for one word:

echo Enter your first name
echo set name=%%5>enter.bat
fc con nul /lb1 /n | date | find " 1: " > temp.bat
call temp.bat
del temp.bat>nul
del enter.bat>nul
echo Your name is %name%.

In this example, because my ENTER.BAT was very simple (set name =%5), I created it by using echo in the second line of the example. Your ENTER.BAT can contain anything you want. If you have a need to process an unknown number of words, just keep using SHIFT to get the next argument. Test each argument after you get it to see if it is blank. If it is, you have no more arguments. Here is an example ENTER.BAT illustrating this:

set name=
:loop
set name=%name% %5
shift
if not "%5"=="" goto loop

A word of explanation is in order. The above 5 code lines are junk (even though they work). A space will be inserted in front of each argument as the "name" value is built (See the space between the %name% and the %5). If only one word is entered, it will have a space in front of it. You can (should) change things so the space goes after each word -- and only if there is another word that will follow it. I didn't because you can't see a space at the end of a line (So how can I show you code you can't see?). Additionally, if the user enters any of the many DOS delimiters (space, comma, equal, semicolon...) or multiple delimiters, you will just convert it to a single space. Maybe you want that. Maybe not. Just keep it in mind.

Generally, if you write a batch file that expects multiple arguments to be entered by the user on a single line, you're asking for trouble. Think about how much easier it is to get user input and verify it's validity if you do it one step at a time. Nobody enjoys retyping an entire line because one word got messed up the first time. But you'll assume mistakes will never happen.and write multiple-argument code anyway. You can reference %5, %6, %7 (for example) as your first, second, and third arguments. You can go all the way to %9 for your fifth argument. Usually it's enough. If you expect more than five arguments, you'll need to use shift.


If you absolutely must capture an entire line of user input and you know it will contain punctuation or multiple spaces, it can be done. Above (far, far above) you saw how fc /lb1 con nul returns an unadulterated user line. Separate it out with FIND and save it to a file. Add that file (concatenate it) to a "line fragment" which assigns the entire line to an environment variable. Assume you have pre-made a line fragment which says set name= (and like all fragments, it has no "return" at the end of the line). For this example, I'll assume the name of the fragment is fragment.txt:

fc /lb1 con nul | find /v "*****" | find /v "Resync failed." | find /v "Comparing files" > temp.txt
copy fragment.txt + temp.txt temp.bat

By doing reverse searches for "******", "Comparing files", and "Resync failed.", I hope that only the user line will appear in temp.txt. If the user types "Foo, King of Bar", then temp.bat will contain:

set name=Foo, King of Bar

Now you can call temp.bat and you'll have your user input line preserved intact in an environment variable.


COPY CON  This used to be very popular in the DOS 5 and 6 days, but it requires ANSI.SYS in any reasonable implementation. I'll cover this method for historical interest. Yes, that means you can stop reading unless you support DOS 6 and older machines. CON, the system console (the keyboard and display), can be treated like a file. If you copy a file to con, it appears on the display. If you copy con to a file, it will put whatever you type into the file. The problem is that there is no way to tell how big the "con" file is. Should a command like
copy con testfile.txt
stop after getting one character? A hundred? A thousand? Since con has no length, we have to designate the end of the file by including the almost-obsolete DOS end-of-file character Ctrl-z (decimal 26, hex 1A). When you're done typing, you hold down the "Ctrl" key, tap the "z" key, let off both keys, then hit "Enter". Great for you, but can you imagine trying to explain that to an office full of users? The solution was to redefine the Enter key so that it sends both a Ctrl-z and Enter. If you have device=ansi.sys in your config.sys file, you can do it. The trouble is that you can't depend on anybody else to have ansi in their config.sys file. There is another problem. If you can redefine Enter to send a Ctrl-z, why can't you redefine it to send a "format c:" command? The answer is that you can. This is known as an ANSI BOMB. If you have ansi loaded and you view a file with such a bomb in DOS (by using the TYPE command), you can be screwed. There are several ansi replacements that warn you about or stop such keyboard redefinitions. PKWare includes one if you register PKZIP (Because those message screens that display when you unzip something in DOS can hide bombs). So virtually nobody uses ansi except in terminal emulators (which are immune to bombs, since they aren't at a DOS prompt).

Anyway, if you really want to do it, the code to redefine the Enter key to "Ctrl-z Enter" is [13;26;13p and the code to return Enter to normal is [13;13p. Both these codes must be preceeded by the "escape" character (which doesn't print, so isn't displayed on this web page). An escape character can be generated in Windows Write by holding down the Alt key while typing in 027 using the numeric keypad. You can generate an escape in DOS EDIT by hitting Ctrl-p, then the Esc key. The classic use is to append the con to a line fragment, creating a batch file. Suppose you had a pre-existing line fragment called userfrag.txt containing set userin=

@echo off
echo Enter your name: [13;26;13p
copy userfrag.txt + con temp.bat
echo [13;13p
call temp.bat
del temp.bat
echo Your name is %userin%

Again, the ansi codes above must be preceeded by an escape character


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.


Batch File Programming: Stupid Useless Tricks

 

Coming under the heading of the "Edison Effect": Thomas Edison could have been the father of electronics when he discovered the electron cloud surrounding heated filaments, but he dismissed it as merely a curiosity. We all bump into curious items, but never pursue thier practical application...

 

Prefacing commands with +

Also works with commas and semicolons. Generally causes the command to treat it's last letter as it's first argument. For example, +MD will create a directory named D. One exception is echo. It treats the entire word as it's first argument.

N:\temp>dir
Volume in drive N is HDA1_BOOT 
Volume Serial Number is 32FF-2953 
Directory of N:\temp
 
.  <DIR> 04-17-97 3:24p . 
.. <DIR> 04-17-97 3:24p .. 
0 file(s) 0 bytes 
2 dir(s) 2,215,936 bytes free
N:\temp>+md
N:\temp>dir
Volume in drive N is HDA1_BOOT 
Volume Serial Number is 32FF-2953 
Directory of N:\temp
 
.  <DIR> 04-17-97 3:24p . 
.. <DIR> 04-17-97 3:24p .. 
D  <DIR> 04-17-97 3:24p d 
0 file(s) 0 bytes 
3 dir(s) 2,215,424 bytes free
N:\temp>+echo hello 
echo hello

Display Windows Revision

Use the undocumented /R option on the VER command.
C:\>ver
Windows 95. [Version 4.00.950]
C:\>ver /r
Windows 95. [Version 4.00.950] 
Revision A 
DOS is in HMA

Display Error Levels

Use the undocumented /Z option on COMMAND
N:\temp>command /z
Microsoft(R) Windows 95 
(C)Copyright Microsoft Corp 1981-1995. 
Return code (ERRORLEVEL): 0 
WARNING: Reloaded COMMAND.COM transient
N:\temp>echo test|find "test">nul 
Return code (ERRORLEVEL): 0
N:\temp>echo qwerty|find "test">nul 
Return code (ERRORLEVEL): 1
N:\temp> exit
 
N:\temp>

Change Floppy Serial Numbers

Use DEBUG to change data on the disk

This example shows how to "turn off" the serial number display for the disk in the A: drive. The digit shown in blue on the "E" command is the one that toggles display of the serial number.
C:\>DEBUG
-L 0 0 0 1
-E 26 00
-W 0 0 0 1
-Q

C:\>DIR A:

Volume in drive A has no label
Directory of A:\

This example shows how to set the serial number for the disk in the A: drive to any value. Notice the numbers you enter (in red) are mirrored from the serial number you'll get.
C:\>debug
-L 0 0 0 1
-E 26 29 78 56 34 12
-W 0 0 0 1
-Q

C:\>DIR A:

Volume in drive A has no label
Volume Serial Number is 1234-5678
Directory of A:\


Using MORE to Concatenate

MORE is the only command I know that accepts two input methods at once. While MORE is usually used with piping or redirection, it can also be supplied with a filename. If you supply both, MORE will "page" both , so you must be careful the combined length will not exceed a page (or you'll be forced to press a key). Notice with the sample files I generated below how MORE places a CR/LF pair at the beginning of each file, but leaves the ends alone.
 

Generation of sample files used in this example

E:\>copy con now.txt 
Now is the time 
for all good men^Z
        1 file(s) copied

E:\>copy con to.txt 
to come to the aid 
of their country^Z 
        1 file(s) copied 




Although my example used
type now.txt|more to.txt
the same results are achieved with
more<now.txt to.txt


Using ATTRIB to Search for a File

ATTRIB with the /S option will search all subdirectories for the designated files. This is much faster than the method of using FIND on the output of DIR /S /B. In fact, it runs about twice as fast as the Windows 95 "Find Files or Folders" function. In addition, it always returns the short "8.3" filename. Frustratingly, it also returns the short path appended to the long filename. For example:

H:\>attrib /s longf*.*
  A   HR     LONGFI~2.TXT  H:\VB5\MSDEVE~1\Long File Name.txt

There is a temptation to use ATTRIB as a long-to-short filename converter. Unfortunately, the attributes do get displayed first. So you can't be sure what position your filename will be in because you don't know how many attributes there are. Since long file names can contain spaces, you can't just start at the end and work backwards either. The good news is that ATTRIB sets errorlevels based on whether or not it found files. So you can at least use it like a recursive IF EXIST:

attrib /s \somefile.txt
if not errorlevel 1 echo I found "somefile.txt" somewhere on the disk

Surviving Abort, Retry, Fail

If you invoke the undocumented /F option on COMMAND, it will automatically answer "F" to any Abort, Retry, Fail? questions. This eliminates the fear of your program hanging if you reference a floppy that isn't inserted. Although the questions are answered automatically, they still appear on screen...

C:\WINDOWS>COMMAND /F
Microsoft(R) Windows 95
   (C)Copyright Microsoft Corp 1981-1995.
C:\WINDOWS>DIR A:
Not ready reading drive A 
Abort, Retry, Fail?
Not ready reading drive A 
Abort, Retry, Fail?Volume in drive A has no label
Not ready reading drive A 
Abort, Retry, Fail?Fail on INT 24
C:\WINDOWS>if exist a:\nul echo hello
Not ready reading drive A 
Abort, Retry, Fail? 
Not ready reading drive ? 
Abort, Retry, Fail? 
C:\WINDOWS> exit
C:\WINDOWS>

Redirection based on IF EXIST

If you use IF EXIST (or IF NOT EXIST) without the required command, you can specify a file for the next command (prompt and all) to be redirected into. Notice the command gets redirected, not the command's output. The redirection only occurs when the IF condition is true and if echo is on. If the condition is false or echo is off, a zero-byte file is generated instead. The bad news is that this only works on Win95 and Win98.

echo on
@if exist nul > test.txt
echo off

In the above example, since NUL always exists, TEST.TXT will contain the string C:\>echo off. Although nobody does it this way, this has obvious applications in capturing prompts without launching a separate DOS shell:

@ctty nul 
prompt set value$Q 
echo on 
if exist nul>temp.bat
hello 
echo off 
prompt $p$g 
ctty con

When the above file is run, it will create a TEMP.BAT with the text set value=hello in it. The ctty nul hides the Bad command or file name error that happens because "hello" is not a valid command.


TRUENAME Simplifies complex paths

Some batch programs may arrive at a path by appending \.. to move around. Afterwards, it may not be clear just where DOS thinks things are. The undocumented TRUENAME command will resolve and simplify 8.3 names whether the files or directories they reference exist or not. TRUENAME even sees through SUBST and JOIN (like anybody uses them nowadays). In the examples below, all commands were entered from the E:\> prompt. The responses all refer to the C: drive.

E:\>TRUENAME C:\PROGRA~1\WINZIP\..\..\WINZIP\PROGRA~1\NUL
C:/NUL
E:\>TRUENAME C:\PROGRA~1\WINZIP\..\..\WINZIP\PROGRA~1\README.TXT
C:\WINZIP\PROGRA~1\README.TXT
E:\>TRUENAME C:\PROGRA~1\WINZIP\..\WINZIP\README.TXT
C:\PROGRA~1\WINZIP\README.TXT

The bad news is that TRUENAME absolutely will not work with long file names. It simply truncates the names until they fit in an 8.3 mask, and will happily give you the bogus name as a result. The good news is that if you change into a directory, running TRUENAME without arguments will give you the real legitimate short name of the directory.


Instant Environment Space

If you need to set lots of environment variables, but you're not sure if there's going to be enough space left for them, you're faced with a tough problem. You have to use the /E option on SHELL in your CONFIG.SYS or with COMMAND to boost your environment space beyond the standard 256 bytes. All well and good on your machine. But if you write for others, maybe they already have a 1024 byte environment. Normally, when you use COMMAND to launch another session, you get a copy of the current environment. But there's an easy way to stop that! Run COMMAND specifying the path for COMMAND.COM (you forgot about that option, didn't you!). You don't have to specify a valid path, but you can if you want to.

E:\>set 
TMP=C:\WINDOWS\TEMP 
TEMP=C:\WINDOWS\TEMP 
PROMPT=$p$g 
winbootdir=C:\WINDOWS 
COMSPEC=C:\WINDOWS\COMMAND.COM 
PATH=C:\WINDOWS;C:\WINDOWS\COMMAND;C:\UTILS; 
windir=C:\WINDOWS
E:\>command . 
Specified COMMAND search directory bad
Microsoft(R) Windows 95
   (C)Copyright Microsoft Corp 1981-1995.
E>set 
PATH=
E>exit
E:\>command c:\windows
Microsoft(R) Windows 95 
   (C)Copyright Microsoft Corp 1981-1995.
E>set 
PATH=
E>exit
E:\>

If you have a heavy-duty batch file, you can call it this way:
command \ /c test.bat
Be careful using this technique, because the entire environment is gone (until you exit). That means no PATH. No access to any commands not in the current directory. You might want to make a copy of the PATH before you jump into the clean session. Notice in the example code below I used "noenvironment" as the command search path. It makes it a little more self-documenting.

@echo off
echo Here is the original environment:
set
echo @echo off > test.bat
path >> test.bat
echo echo. >> test.bat
echo echo Here is the clean environment: >> test.bat
echo set >> test.bat
command noenvironment /c test.bat
del test.bat

ECHOing the words ON and OFF

Echo is often used to create secondary batch files, Basic files, and user prompts. Very rarely, you'll need to echo a line that starts with on or off. If so, you can expand on a trick used to create blank lines. You probably already know that echo. (echo followed by a period with no space between) will result in a blank line, but did you know that you can substitute any of these five characters / \ [ ] + instead of the period as well? It turns out that echo followed by any of those six characters will result in echo treating on and off as simple words:

E:\>echo on error goto done
ON
 
E:\>echo.on error goto done
on error goto done
 
E:\>echo on
 
E:\>echo.on
on
 
E:\>echo off - opposite of on
OFF
 
E:\>echo.off - opposite of on
off - opposite of on

Running DOS 5 & 6 Under Windows 95

Now, this is the stupidest thing you could ever want to do. The only time I ever do it is when I want a quick test of some obsolete feature for a compatibility check. Otherwise I reboot my machine (I love System Commander!) into the appropriate OS. Still, to quote Douglas Adams, it's "Mostly harmless". You can see by the picture that I am running 5 different versions of DOS simultaneously. Try not to do too much unless you like seeing Incorrect DOS version on every other command. All you need to do is copy and rename your command.com files ( I picked DOS500, DOS600, etc.). Then use SETVER to put the new names in the version table, for example

setver dos500.com 5.00
setver dos600.com 6.00
setver dos620.com 6.20
setver dos622.com 6.22

After you reboot, you'll be able to use the old versions of DOS simultaneously just like I do. Just don't try to run them from the command line or you'll lock up your DOS window. Either double-click them with Explorer, put them in your Start Menu, or use START from the command line to run them in a separate window.


Get Everything Before Something Else
No, there is no better way to describe it. If you have a string like 2:34:56.78p and you want to take action based on everything before the first colon, you can do it without parsing it character-by-character. The only restriction is that the "colon" has to be one of these ten special characters:  [  :  ,  .  /  \  ;  + =  ]    What you do is GOTO to a label in your program and use the string as the specified destination. Except it won't actually go there. It only reads up to the first special character. Suppose I had these two batch files:

::TEST1.BAT
@echo off
call test2.bat 2:34:56.78p

::TEST2.BAT
@echo off
goto LBL%1
:LBL
echo I didn't go to a label!
goto DONE
:LBL1
echo At label 1
goto DONE
:LBL2
echo At label 2
goto DONE
:DONE

When TEST1 runs, it calls TEST2, passing it 2:34:56.78p as an argument. TEST2 then has a GOTO command which you might think would get interpreted as GOTO LBL2:34:56.78p The amazing thing is that it actually goes to LBL2! Everything from the colon on to the right got ignored! So this is a way to make a jump in your program based on a drive letter, hour, month or who knows what else.


 

Command shell overview for Windows XPMicrosoft Windows graphic

The command shell is a separate software program that provides direct communication between the user and the operating system. The non-graphical command shell user interface provides the environment in which you run character-based applications and utilities. The command shell executes programs and displays their output on the screen by using individual characters similar to the MS-DOS command interpreter Command.com. The Windows XP command shell uses the command interpreter Cmd.exe, which loads applications and directs the flow of information between applications, to translate user input into a form that the operating system understands.

You can use the command shell to create and edit batch files (also called scripts) to automate routine tasks. For example, you can use scripts to automate the management of user accounts or nightly backups. You can also use the Windows Script Host, CScript.exe, to run more sophisticated scripts in the command shell. You can perform operations more efficiently by using batch files than you can by using the user interface. Batch files accept all commands that are available at the command line. For more information about batch files and scripting, see Using batch files

You can customize the command prompt window for easier viewing and to increase control over how you run programs. For more information about customizing the command prompt window, see To configure the command prompt

Using command syntax

Syntax appears in the order in which you must type a command and any parameters that follow it. The following example of the xcopy command illustrates a variety of syntax text formats:

xcopy Source [Destination] [/w] [/p] [/c] [/v] [/q] [/f] [/l] [/g] [/d[:mm-dd-yyyy]] [/u] [/i] [/s [/e]] [/t] [/k] [/r] [/h] [{/a|/m}] [/n] [/o] [/x] [/exclude:file1[+[file2]][+[file3]] [{/y|/-y}] [/z]

The following table explains how to interpret the different text formats.

Formatting legend

Format

Meaning

Italic

Information that the user must supply

Bold

Elements that the user must type exactly as shown

Ellipsis (...)

Parameter that can be repeated several times in a command line

Between brackets ([])

Optional items

Between braces ({}); choices separated by pipe (|). Example: {even|odd}

Set of choices from which the user must choose only one

Courier font

Code or program output

Using multiple commands and conditional processing symbols

You can run multiple commands from a single command line or script using conditional processing symbols. When you run multiple commands with conditional processing symbols, the commands to the right of the conditional processing symbol act based upon the results of the command to the left of the conditional processing symbol. For example, you might want to run a command only if the previous command fails. Or, you might want to run a command only if the previous command is successful.

You can use the special characters listed in the following table to pass multiple commands.

Character

Syntax

Definition

& [...]

command1 & command2

Use to separate multiple commands on one command line. Cmd.exe runs the first command, and then the second command.

&& [...]

command1 && command2

Use to run the command following && only if the command preceding the symbol is successful. Cmd.exe runs the first command, and then runs the second command only if the first command completed successfully.

|| [...]

command1 || command2

Use to run the command following || only if the command preceding || fails. Cmd.exe runs the first command, and then runs the second command only if the first command did not complete successfully (receives an error code greater than zero).

( ) [...]

(command1 & command2)

Use to group or nest multiple commands.

; or ,

command1 parameter1;parameter2

Use to separate command parameters.

note Note

·                     The ampersand (&), pipe (|), and parentheses ( ) are special characters that must be preceded by the escape character (^) or quotation marks when you pass them as arguments.

·                     If a command completes an operation successfully, it returns an exit code of zero (0) or no exit code. For more information about exit codes, see Microsoft Windows Resource Kits

Nesting command shells

You can nest command shells within Cmd.exe by opening a new instance of Cmd.exe at the command prompt. By default, each instance of Cmd.exe inherits the environment of its parent Cmd.exe application. By nesting instances of Cmd.exe, you can make changes to the local environment without affecting the parent application of Cmd.exe. This allows you to preserve the original environment of Cmd.exe and return to it after you terminate the nested command shell. The changes you make in the nested command shell are not saved.

To nest a command shell, at the command prompt, type:

cmd

A message similar to the following appears:

Microsoft (R) Windows XP (TM)
(C) Copyright 1985-2001 Microsoft Corp.

To close the nested command shell, type exit.

You can localize changes even further in an instance of Cmd.exe (or in a script) by using the setlocal and endlocal commands. Setlocal creates a local scope and endlocal terminates the local scope. Any changes made within the setlocal and endlocal scope are discarded, thereby leaving the original environment unchanged. You can nest these two commands to a maximum of 32 levels. For more information about the setlocal and endlocal commands, see Setlocal and Endlocal

Using environment variables with Cmd.exe

The Cmd.exe command-shell environment is defined by variables that determine the behavior of the command shell and the operating system. You can define the behavior of the command-shell environment or the entire operating system environment by using two types of environment variables, system and local. System environment variables define the behavior of the global operating system environment. Local environment variables define the behavior of the environment of the current instance of Cmd.exe.

System environment variables are preset in the operating system and available to all Windows XP processes. Only users with administrative privileges can change system variables. These variables are most commonly used in logon scripts.

Local environment variables are only available when the user for whom they were created is logged on to the computer. Local variables set in the HKEY_CURRENT_USER hive are valid only for the current user, but define the behavior of the global operating system environment.

The following list describes the various types of variables in descending order of precedence:

1.                  Built-in system variables

2.                  System variables found in the HKEY_LOCAL_MACHINE hive

3.                  Local variables found in the HKEY_CURRENT_USER hive

4.                  All environment variables and paths set in the Autoexec.bat file

5.                  All environment variables and paths set in a logon script (if present)

6.                  Variables used interactively in a script or batch file

In the command shell, each instance of Cmd.exe inherits the environment of its parent application. Therefore, you can change the variables in the new Cmd.exe environment without affecting the environment of the parent application.

The following table lists the system and local environment variables for Windows XP.

Variable

Type

Description

%ALLUSERSPROFILE%

Local

Returns the location of the All Users Profile.

%APPDATA%

Local

Returns the location where applications store data by default.

%CD%

Local

Returns the current directory string.

%CMDCMDLINE%

Local

Returns the exact command line used to start the current Cmd.exe.

%CMDEXTVERSION%

System

Returns the version number of the current Command Processor Extensions.

%COMPUTERNAME%

System

Returns the name of the computer.

%COMSPEC%

System

Returns the exact path to the command shell executable.

%DATE%

System

Returns the current date. Uses the same format as the date /t command. Generated by Cmd.exe. For more information about the date command, see Date

%ERRORLEVEL%

System

Returns the error code of the most recently used command. A non zero value usually indicates an error.

%HOMEDRIVE%

System

Returns which local workstation drive letter is connected to the user's home directory. Set based on the value of the home directory. The user's home directory is specified in Local Users and Groups.

%HOMEPATH%

System

Returns the full path of the user's home directory. Set based on the value of the home directory. The user's home directory is specified in Local Users and Groups.

%HOMESHARE%

System

Returns the network path to the user's shared home directory. Set based on the value of the home directory. The user's home directory is specified in Local Users and Groups.

%LOGONSEVER%

Local

Returns the name of the domain controller that validated the current logon session.

%NUMBER_OF_PROCESSORS%

System

Specifies the number of processors installed on the computer.

%OS%

System

Returns the operating system name. Windows 2000 displays the operating system as Windows_NT.

%PATH%

System

Specifies the search path for executable files.

%PATHEXT%

System

Returns a list of the file extensions that the operating system considers to be executable.

%PROCESSOR_ARCHITECTURE%

System

Returns the chip architecture of the processor. Values: x86 , IA64.

%PROCESSOR_IDENTFIER%

System

Returns a description of the processor.

%PROCESSOR_LEVEL%

System

Returns the model number of the processor installed on the computer.

%PROCESSOR_REVISION%

System

Returns the revision number of the processor.

%PROMPT%

Local

Returns the command prompt settings for the current interpreter. Generated by Cmd.exe.

%RANDOM%

System

Returns a random decimal number between 0 and 32767. Generated by Cmd.exe.

%SYSTEMDRIVE%

System

Returns the drive containing the Windows XP root directory (that is, the system root).

%SYSTEMROOT%

System

Returns the location of the Windows XP root directory.

%TEMP% and %TMP%

System and User

Returns the default temporary directories that are used by applications available to users who are currently logged on. Some applications require TEMP and others require TMP.

%TIME%

System

Returns the current time. Uses the same format as the time /t command. Generated by Cmd.exe. For more information about the time command, see Time

%USERDOMAIN%

Local

Returns the name of the domain that contains the user's account.

%USERNAME%

Local

Returns the name of the user who is currently logged on.

%USERPROFILE%

Local

Returns the location of the profile for the current user.

%WINDIR%

System

Returns the location of the operating system directory.

Setting environment variables

Use the set command to create, change, delete, or display environment variables. The set command alters variables in the current shell environment only.

To view a variable, at a command prompt, type:

set VariableName

To add a variable, at a command prompt, type:

set variablename=value

To delete a variable, at a command prompt, type:

set VariableName=

You can use most characters as variable values, including white space. If you use the special characters <, >, |, &, or ^, you must precede them with the escape character (^) or quotation marks. If you use quotation marks, they are included as part of the value because everything following the equal sign is taken as the value. Consider the following examples:

·                     To create the variable value new&name, type:

set varname=new^&name

·                     To create the variable value "new&name", type:

set varname="new&name"

·                     If you type set varname=new&name at the command prompt, an error message similar to the following appears:

"'name' is not recognized as an internal or external command, operable program or batch file."

Variable names are not case-sensitive. However, set displays the variable exactly as you typed it. You can combine uppercase and lowercase letters in your variable names to make your code more readable (for example, UserName).

note Note

·                     The maximum individual environment variable size is 8192bytes.

·                     The maximum total environment variable size for all variables, which includes variable names and the equal sign, is 65,536KB.

Substituting environment variable values

To enable the substitution of variable values at the command line or in scripts, enclose the variable name in percent signs (that is, %variablename%). By using percent signs, you ensure that Cmd.exe references the variable values instead of making a literal comparison. After you define variable values for a variable name, enclose the variable name in percent signs. Cmd.exe searches for all instances of the variable name and replaces it with the defined variable value. For example, if you create a script that contains different values (for example, user names) and you want to define the USERNAME environment variable for each user with these values, you can write one script using the variable USERNAME enclosed in percent signs. When you run this script, Cmd.exe replaces %USERNAME% with the variable values, which eliminates the need to perform this task manually for each user. Variable substitution is not recursive. Cmd.exe checks variables once. For more information about variable substitution, see For and Call


Appendix

Sample Win9x Batch Files (http://www.ericphelps.com/batch/samples/samples.htm)

 

User Input

Get user input from batch file
Sure, you've done it, but can you do it without having to hit a Ctrl-Z, without ANSI, without a debug script, and without a separate "set" file?

rem  This batch file gets a character or word of user input and 
rem  returns it in the environment variable VALUE. Two tricks are 
rem  used to accomplish this: 
rem
rem  (1) The FC (File Compare) command is used to compare two standard 
rem      devices -- NUL (nothing) and CON (the console). The /LB1 option
rem      is used to insure only one line is compared, and the /N numbers  
rem      that output line with a "1:", making it easier for us to find.
rem      FC will output immediately after the user hits "Enter" (because
rem      of the /LB1), and will give us a total of 7 lines of output. 
rem      Of these 7 lines, the one starting with "1:" is the one we want.
rem
rem  (2) The DATE command is used only because it always returns the
rem      phrase "Enter new date (mm-dd-yy): " followed by whatever was
rem      piped into it. Why is this format important?  Obviously, it
rem      has nothing to do with setting the date!  Well, we will be
rem      piping it into a batch file and running it. When that batch 
rem      file runs, it will try to execute the first word (Enter) as if 
rem      it were a valid command, and pass everything else as arguments. 
rem      Since we have created a valid (batch file) command called ENTER, 
rem      this will actually work! We just have to make sure that ENTER.BAT
rem      is set up to handle the arguments that will be passed to it!
 
 
echo This is a test. Please enter "y" or "n"
fc con nul /lb1 /n | date | find "1:" > en#er.bat
echo set value=%%5> enter.bat
call en#er.bat
del en?er.bat > nul
if "%value%"=="n" echo You entered "n"
if "%value%"=="y" echo You entered "y"
set value=
 
 
rem      See PC Magazine V14N12 June 27, 1995 page 248 for further 
rem      information. The PC Magazine article suggests using a 
rem      permanent ENTER.BAT which can accept a line 
rem      of user input (complete with spaces between words!). 
rem      My version is meant to be inserted into any batch file 
rem      and will create a simple one-line temporary ENTER.BAT in 
rem      the default directory and on the fly as needed.
rem      http://www.ericphelps.com

 

Get user input and mask passwords with HTML
HTML forms can mask passwords as you enter them, so use forms! Here I show you how to have your batch file create a username/password form, then use Windows Scripting to read the contents of that form and transfer the results back into a batch file. As shown, it uses the environment, so you may need to modify things if your target computers don't have room.

Get user input and mask password with HTA
HTML Applications (HTA files) can do everything ordinary HTML can, but there is no need to separate the HTML from the scripting in order to save the results to the hard drive. Like the above HTML method, this creates a batch file named "userin.bat" in the "temp" directory which can be called by your batch file to retrieve the data.

Get (and mask) passwords with QBASIC
QBASIC allows you to grab user input without echoing it to the screen. This makes it easy to throw an asterisk up for every character that gets entered. Again, this code stores the password in the environment for your batch file to use. FYI, Win9x boxes don't come with QBASIC installed by default. See here for how to get QBASIC and here for how to install it automatically.

Get user input from an NT batch file
All the old tricks fail. Luckily, QBASIC can be used and is standard equipment in NT. Windows Scripting works better if you have it.

  Getting user input with NT can be a problem 
   because most user input routines use the CON 
   and NUL device. NT, unfortunately, doesn't handle 
   these devices as files (like Win95 and DOS do). 
   Luckily, NT comes standard with QBASIC! Shown 
   here is a way to create a simple QBASIC program 
   that takes the user input and creates a temporary 
   batch file. When the batch file is run, an 
   environmental variable is set containing the 
   user input. 
 
---------------------------------------------------
@echo off
echo OPEN "~usrin.bat" FOR OUTPUT AS #1> ~usrin.bas
echo INPUT "Enter your name ", sUsrin$>> ~usrin.bas
echo PRINT #1, "set usrin="; sUsrin$>> ~usrin.bas
echo CLOSE #1>> ~usrin.bas
echo SYSTEM>> ~usrin.bas
qbasic /run ~usrin.bas
call ~usrin.bat
del ~usrin.bat
del ~usrin.bas
echo Your name is %usrin%
pause
cls
---------------------------------------------------
 
    I've had scattered reports that the above code 
    doesn't run on code page 437 (US) but works 
    on code page 850 (Multilingual). But I use 
    code page 437, and the code works identically 
    for me under Win95 and NT4. Just FYI.
 
    Eric Rose, someone who knows NT better than I 
    do, found that if you have problems with 
    QBASIC using expanded memory in ways NT 
    finds unacceptable, you can make everybody 
    happy by adding this line
 
---------------------------------------------------
set RTVMEXP=0
---------------------------------------------------
 
    to the beginning of the batch file (actually
    the second line, just after the "@echo off"
    line).
 
    In the above example, I create the QBASIC 
    code as it is needed. You can speed things up 
    considerably by creating the code ahead of time 
    and having it kept as a permanent item. Here is 
    the code rewritten as two separate files. 
    First, the batch file:
 
---------------------------------------------------
@echo off
qbasic /run userin.bas
call ~userin.bat
del ~userin.bat
echo Your name is %userin%
---------------------------------------------------
 
    Now the QBASIC file I call "USERIN.BAS"
 
---------------------------------------------------
OPEN "~userin.bat" FOR OUTPUT AS #1
INPUT "Enter your name ", sUsrin$
PRINT #1, "set userin="; sUsrin$
CLOSE #1
SYSTEM
---------------------------------------------------
 
    If you've kept your service packs up to date, 
    you got the Windows Scripting Host installed 
    when SP4 came out. If you have scripting, 
    this batch file should provide you with 
    another prettier alternative:
 
---------------------------------------------------
@echo off
 > ~userin.vbs echo strUserIn = InputBox("Enter Data")
>> ~userin.vbs echo Set fs = Wscript.CreateObject("Scripting.FileSystemObject")
>> ~userin.vbs echo strFileName = fs.BuildPath(Wscript.ScriptFullName ^& "\..", "~userin.bat")
>> ~userin.vbs echo strFileName = fs.GetAbsolutePathName(strFileName)
>> ~userin.vbs echo Set ts = fs.OpenTextFile(strFileName, 2, True)
>> ~userin.vbs echo ts.WriteLine "set userin=" ^& strUserIn
>> ~userin.vbs echo ts.Close
start /w wscript.exe ~userin.vbs
del ~userin.vbs
call ~userin.bat
del ~userin.bat
echo You entered %USERIN%
---------------------------------------------------
 
    Again, here is the above scripting version rewritten 
    as two separate files. First the batch file:
 
---------------------------------------------------
start /w wscript.exe userin.vbs
call ~userin.bat
del ~userin.bat
echo You entered %USERIN%
---------------------------------------------------
 
    Now the script file I call "userin.vbs"
 
---------------------------------------------------
strUserIn = InputBox("Enter Data")
Set fs = Wscript.CreateObject("Scripting.FileSystemObject")
strFileName = fs.BuildPath(Wscript.ScriptFullName & "\..", "~userin.bat")
strFileName = fs.GetAbsolutePathName(strFileName)
Set ts = fs.OpenTextFile(strFileName, 2, True)
ts.WriteLine "set userin=" & strUserIn
ts.Close
---------------------------------------------------
 
    While the "two separate files" version 
    above will run on any machine (so far) 
    that has scripting, the single file 
    version I showed only works on NT. 
    That's because the ampersand has special 
    meaning under NT. When the batch file 
    tries to "echo" a "&" character, NT 
    requires that a caret precede it:
 
---------------------------------------------------
>> ~userin.vbs echo ts.WriteLine "set userin=" ^& strUserIn
---------------------------------------------------
 
    The same line written for a Win9x machine would be:
 
---------------------------------------------------
>> ~userin.vbs echo ts.WriteLine "set userin=" & strUserIn
---------------------------------------------------
 
    If you have to write code that works 
    on both machines, you can test for the 
    behavior (which is safer than testing 
    for the OS). Here is code that will run 
    on NT and Win9x:
 
---------------------------------------------------
@echo off
:: This batch file illustrates how to create and run a script
:: file from a batch file. This uses scripting to solve the
:: common "How do I get user input" problem. This should work on
:: Win9x and NT boxes as long as they have scripting installed!
 
:: First test to see if we are on NT or similar OS
:: The ony difference is how they handle the ampersand
 > ~userin.vbs echo 1234&rem
type ~userin.vbs | find "rem" > nul
if errorlevel 1 goto WINNT
goto WIN9X
 
:WIN9X
 > ~userin.vbs echo strUserIn = InputBox("Enter Data")
>> ~userin.vbs echo Set fs = Wscript.CreateObject("Scripting.FileSystemObject")
>> ~userin.vbs echo strFileName = fs.BuildPath(Wscript.ScriptFullName & "\..", "~userin.bat")
>> ~userin.vbs echo strFileName = fs.GetAbsolutePathName(strFileName)
>> ~userin.vbs echo Set ts = fs.OpenTextFile(strFileName, 2, True)
>> ~userin.vbs echo ts.WriteLine "set userin=" & strUserIn
>> ~userin.vbs echo ts.Close
goto RUN
 
:WINNT
 > ~userin.vbs echo strUserIn = InputBox("Enter Data")
>> ~userin.vbs echo Set fs = Wscript.CreateObject("Scripting.FileSystemObject")
>> ~userin.vbs echo strFileName = fs.BuildPath(Wscript.ScriptFullName ^& "\..", "~userin.bat")
>> ~userin.vbs echo strFileName = fs.GetAbsolutePathName(strFileName)
>> ~userin.vbs echo Set ts = fs.OpenTextFile(strFileName, 2, True)
>> ~userin.vbs echo ts.WriteLine "set userin=" ^& strUserIn
>> ~userin.vbs echo ts.Close
goto RUN
 
:RUN
:: Now run the created script
start /w wscript.exe ~userin.vbs
del ~userin.vbs
 
:: Now call the created batch file
call ~userin.bat
del ~userin.bat
 
:: Now display the data!
echo You entered %USERIN%
pause
cls

 

 

Accept arguments in any order
Simon Richardson shows how to use structured batch programming (as best as batch can do it) to collect arguments. Then I show how to break that structure.

Accept arguments in any order
Fred Fisher said he could improve on the above method to collect arguments. I think you'll agree he did! He does it with a few more IFs and a lot less GOTOs. He demonstrates three separate argument types. One is a switch, another takes one argument, and the last takes two separate arguments. You can easily simplify (or extend) the code for any number of arguments each taking any number of parameters. Then, just to show that he could do the same thing by using subroutines, Fred sent along another example.

How to get around the "Do you really want to do this?" prompt.
Have your batch file send the desired answer..

How to simulate typing to any program.
Send your program keystrokes whether it is a DOS or Windows program. This isn't "User Input", but is more along the lines of simulating a user who isn't there. Great for automating programs that weren't designed to be automated.
 

A common question I get is how to send keystrokes to a running program. 
 
______________________________________________________________________
 
 
If the program is a DOS program, you'll probably need to use a DOS 
keystroke TSR program. Suggestions:
ScanCode:
http://www.simtel.net/pub/pd/3741.html
Other keyboard utilities:
http://www.simtel.net/pub/msdos/keyboard/
 
If you use ScanCode, the manual should be avoided! Why? Because it is 
too big! Just type "scancode /?" at a DOS prompt and read the short 
help screen. Most of the time, that's all the help you'll need.
 
______________________________________________________________________
 
 
You can send keystrokes (with mixed results depending on your operating 
system) to a DOS window from the Windows Scripting "CSCRIPT" program. 
You may need to upgrade your scripting if you have the old version that 
shipped on the Win 98 CDROM. Try a VBS script like this:
 
Wscript.Stdout.WriteLine "dir"
Wscript.Sleep 5000
Wscript.Stdout.WriteLine "echo on"
 
Test the above script with a batch file like this:
 
Cscript.exe //NoLogo test.vbs | command.com
 
(Use cmd.exe instead of command.com on an XP/NT/2000 system) On a Win9x 
box, nothing will happen for 5 seconds, then the DIR and ECHO ON commands 
will be sent right next to each other. On a Win2000 box, DIR will be sent, 
then a five-second delay, then ECHO ON will be sent. Obviously this is 
more useful under Windows 2000! Use "Write" instead of "WriteLine" if you 
don't want a carriage return added to your text. And I bet you already 
figured out that the "Sleep" value is in milliseconds.
 
______________________________________________________________________
 
 
For a generic solution that works on DOS windows or regular Windows, 
you might want to look at AutoIt from http://www.hiddensoft.com/. It's 
a keystroke automater on steroids. 
 
______________________________________________________________________
 
 
If you need to send keystrokes to an already-running Windows program, 
you can use Windows Scripting. I have a scripting web page here: 
http://www.ericphelps.com/scripting/index.htm
You'll need to upgrade your version of the Windows Scripting Host 
if you have what shipped with the Windows 98 CDROM. If you have 
Windows 2000 or newer, you should be okay.
 
Basically, this one-line VBS script (call it "SendKeys.vbs"): 
 
CreateObject("WScript.Shell").SendKeys Wscript.Arguments(0) 
 
can be called from a batch file like this: 
 
start /w SendKeys.vbs "blah, blah, blah" 
 
But there is a little problem... The script will basically type 
keystrokes, but no guarantee those keystrokes will stay in the 
program you want! So you may need to insure the program has focus 
by activating the application BEFORE you use SendKeys with the 
"AppActivate" method. Let's call this one "AppActivate.vbs": 
 
CreateObject("WScript.Shell").AppActivate Wscript.Arguments(0) 
 
Use it like this: 
 
start /w AppActivate.vbs "Notepad" 
 
The AppActivate must be passed the beginning, end, or complete 
text of a window title. "Notepad", for example, is at the end of 
the title bar. You use whatever is appropriate for the program 
you are controlling. 
 
If you need a time delay, here is a VBS script (call it Sleep.vbs): 
 
Wscript.Sleep Wscript.Arguments(0) * 1000 
 
That will delay for as many seconds as you tell it: 
 
start /w Sleep.vbs 10 
 
You can run each line from a batch file like I show above (using 
the start command each time), or you could put all those lines 
together in one large VBS file. If you want to put them in one 
large VBS file, just replace the "Wscript.Arguments(0)" part 
with your value like this: 
 
start /w AppActivate.vbs "Telnet" 
Wscript.Sleep 1*1000 
CreateObject("WScript.Shell").SendKeys "username{ENTER}" 
Wscript.Sleep 2*1000 
start /w AppActivate.vbs "Telnet" 
Wscript.Sleep 1*1000 
CreateObject("WScript.Shell").SendKeys "password{ENTER}" 
 
When you get more advanced, you'll find you don't need to create the 
objects every time. But I'll leave that for you to discover if you 
decide to learn more about Visual Basic and Scripting.
 
______________________________________________________________________
 
 
 
Here is the help file for the special keys you need to know to use 
SendKeys (copied directly from the Microsoft help file):
 
Each key is represented by one or more characters. To specify a 
single keyboard character, use the character itself. For example, 
to represent the letter A, use "A" for string. To represent more 
than one character, append each additional character to the one 
preceding it. To represent the letters A, B, and C, use "ABC" for 
string. The plus sign (+), caret (^), percent sign (%), tilde (~), 
and parentheses ( ) have special meanings to SendKeys. To specify 
one of these characters, enclose it within braces ({}). For 
example, to specify the plus sign, use {+}. Brackets ([ ]) have no 
special meaning to SendKeys, but you must enclose them in braces. 
In other applications, brackets do have a special meaning that may 
be significant when dynamic data exchange (DDE) occurs. To specify 
brace characters, use {{} and {}}. 
 
To specify characters that aren't displayed when you press a key, 
such as ENTER or TAB, and keys that represent actions rather than 
characters, use the codes shown below: 
 
Key Code 
BACKSPACE {BACKSPACE}, {BS}, or {BKSP} 
BREAK {BREAK} 
CAPS LOCK {CAPSLOCK} 
DEL or DELETE {DELETE} or {DEL} 
DOWN ARROW {DOWN} 
END {END} 
ENTER {ENTER}or ~ 
ESC {ESC} 
HELP {HELP} 
HOME {HOME} 
INS or INSERT {INSERT} or {INS} 
LEFT ARROW {LEFT} 
NUM LOCK {NUMLOCK} 
PAGE DOWN {PGDN} 
PAGE UP {PGUP} 
PRINT SCREEN {PRTSC} 
RIGHT ARROW {RIGHT} 
SCROLL LOCK {SCROLLLOCK} 
TAB {TAB} 
UP ARROW {UP} 
F1 {F1} 
F2 {F2} 
F3 {F3} 
F4 {F4} 
F5 {F5} 
F6 {F6} 
F7 {F7} 
F8 {F8} 
F9 {F9} 
F10 {F10} 
F11 {F11} 
F12 {F12} 
F13 {F13} 
F14 {F14} 
F15 {F15} 
F16 {F16} 
 
To specify keys combined with any combination of the SHIFT, CTRL, 
and ALT keys, precede the key code with one or more of the 
following codes: 
 
Key Code 
SHIFT + 
CTRL ^ 
ALT % 
 
To specify that any combination of SHIFT, CTRL, and ALT should be 
held down while several other keys are pressed, enclose the code 
for those keys in parentheses. For example, to specify to hold down 
SHIFT while E and C are pressed, use "+(EC)". To specify to hold down 
SHIFT while E is pressed, followed by C without SHIFT, use "+EC". 
 
To specify repeating keys, use the form {key number}. You must put a 
space between key and number. For example, {LEFT 42} means press the 
LEFT ARROW key 42 times; {h 10} means press H 10 times. 


 

Timing

Make a time delay
Put your computer to sleep for a while. At least put the particular DOS window you are in to sleep.

    How to do a delay.
    I used to think it couldn't be done. Then I thought it 
    can't be done WELL! But I know several ways to do it. 
    Each with it's own problems. But first a cautionary note: 
    Most people who THINK they need a time delay actually want 
    to give some other program time to finish. That is best 
    accomplished with the START command using the /w option: 
 
START /W MYPROGRAM.EXE
    _______________________________________________________
 
    The first way to make a time delay uses the CHOICE 
    command. CHOICE has the "/t" option which lets it 
    automatically select for you after a time delay. Try 
    this in a DOS window now:
 
choice /ty,10
 
    If you do NOTHING, it will wait ten seconds and enter a 
    "y" for you. The problem is that if you hit anything 
    other than the "y" or "n" key in those ten seconds, it 
    will stop the timer and wait FOREVER for you to hit the 
    correct key (a "y" or an "n" in this case). On the other 
    hand, if you hit a correct key in those ten seconds, it 
    stops the timer and continues IMMEDIATELY. Now generally, 
    when you code for ten seconds, you want ten seconds. Not 
    immediately, and not forever. If you can keep the end 
    user's hands off the keyboard, things work just fine.
    _______________________________________________________
 
    Now, there is approach that almost eliminates the keyboard 
    problems. You can force CHOICE to ignore the keyboard by 
    redirecting it's input like this:
 
rem | choice /ty,10
 
    Nothing you do will stop or hang the time delay. But keys 
    entered WILL get passed on to whatever happens next. Just 
    keep it in mind. And remember you can use the /n and > nul
    tricks to keep your screen clean:
 
rem | choice /n /ty,10 > nul
 
    _______________________________________________________
 
    There is another workaround to the keyboard problem: Make 
    all keys acceptable:
 
choice /n /c±-1234567890qwertyuiopasdfghjklzxcvbnm /t±,10 > nul
if not errorlevel 1 echo You hit a key and aborted the timeout!
 
    Okay, I did several things at once in the above code. 
    The ± character is one of the "extended" characters (177) 
    that nobody is likely to enter from a keyboard. I made 
    that extended character the default character. I also 
    made it the first character. That way I know if I get 
    an errorlevel that isn't a one, some other key was entered. 
    And I made every key I could a legal key to try to stop 
    the "forever" problem. 
 
    FYI, the CHOICE command is not generally available under NT. 
    You can get it on the disk version of the Resource Kit, but
    not on the download version. If you really want to implement
    this Win9x solution on NT, you can always steal a copy of 
    CHOICE.EXE from a Win9x box. All reports I've heard say it
    works just fine.
 
    _______________________________________________________
 
    People running NT will be quickly frustrated by the 
    above examples.  QBASIC, however, runs on Win9x and 
    NT and offers a more universal solution. Try using the 
    QBASIC "sleep" command. In the batch file below, I 
    construct a simple two-line QBASIC program and run it. 
    Couldn't be easier. The problem is that pressing any key 
    immediately ends the time delay. At least there is no 
    "forever" problem.
 
@echo off
echo Starting!
echo sleep 10> sleep.bas
echo system>> sleep.bas
qbasic /run sleep.bas
echo Done!
del sleep.bas
 
    _______________________________________________________
 
    And here is the good QBASIC way, but this time using 
    the "on timer" function.
 
@echo off
echo Starting!
echo.on timer(10) gosub bail> sleep.bas
echo timer on>> sleep.bas
echo while -1>> sleep.bas
echo wend>> sleep.bas
echo system>> sleep.bas
echo bail:>> sleep.bas
echo system>> sleep.bas
qbasic /run sleep.bas
echo Done!
del sleep.bas
 
    The period after the echo command is what you have to 
    do to actually echo the word "on" in Win9x.
 
    _______________________________________________________
 
    One of the problems with the above methods is that 
    they all send CPU usage to 100%. Actually, this is 
    less of a problem than you might think if you are 
    working with short, tolerable time delays. If you 
    need a low a low-impact time delay, you're better 
    off switching to Windows Scripting. The 
    "Wscript.Sleep" command allows you to specify a 
    sleep time in milliseconds. So a time delay of 
    10 seconds would need a 10000 millisecond value. 
    Here is batch code for a ten-second delay that 
    creates the needed scripting file:
 
@echo off
echo Starting!
echo Wscript.Sleep 10000> sleep.vbs
start /w wscript.exe sleep.vbs
echo Done!
del sleep.vbs

 

 

Time delay and For-Next loops
Demonstrates two things: using CHOICE to effect a one-second time delay, and using  environment variables to track nested "for-next" loops in batch files (yes, you can!) to multiply the one second up to sixty seconds (actually sixty seconds plus batch processing time). And what did I decide this file should do with it's one-minute delay? Why, display the time, of course!

Run a specified program only on certain days at boot time.
This demo makes use of two tricks: It reads a custom INI file, and it puts the current day into the environment.
 

@echo off
:: This batch file allows you to run programs on certain days.
:: Have this TODAY.BAT program CALLed from your AUTOEXEC.BAT.
:: TODAY.BAT will read entries in a TODAY.INI file you must make.
:: The entries in the TODAY.INI should look like this:
:: 10-12-1999=DeleteOldDocs.bat
:: 10-13-1999=Scandisk.bat
:: 10-14-1999=PrintReports.bat
 
:: Put the date into the DATE environment variable
:: Response to DATE command should be like
:: Current date is Sun 10-12-1997
:: Enter new date (mm-dd-yy):
@echo.|date|find /i "current">#urrent.bat
@echo set date=%%4>current.bat
call #urrent.bat
del ?urrent.bat
 
:: Put the command for today into TODAYSCOMMAND variable
:: Assumes existence of TODAY.INI in current directory
find "%date%=" today.ini | sort /r | date | find "=" > en#er.bat
@echo set todayscommand=%%5> enter.bat
call en#er.bat
del en?er.bat > nul
 
:: Cleanup - delete DATE variable and INI entry (so it only runs once)
type today.ini|find /v "%date%">today.ini
set date=
 
:: Run today's command
call %todayscommand%
set todayscommand=
 
 
:: _____________________________________________________
:: If the command for the day requires user input, for
:: example, you have to hit enter or answer yes or no, 
:: you should run a separate batch file instead of
:: running the command directly. Assume you want to run
:: CHKDSK with the /F option to fix errors. If it finds
:: errors, it will ask you to type "y" or "n".  You can
:: create a batch file which will do this for you:
:: ECHO Y|CHKDSK /F
:: If all you need is to press "Enter", you can do this:
:: ECHO.|CHKDSK /F
:: If you need to press "y", AND THEN HIT "Enter", it
:: gets more complicated. You have to create what is
:: generally called a "script" containing the exact
:: keystrokes you need to press. You can create it 
:: ahead of time, or as needed like this:
:: ECHO Y>SCRIPT.TXT
:: ECHO.>>SCRIPT.TXT
:: TYPE SCRIPT.TXT|CHKDSK /F
:: Notice the first line had only one ">", but the
:: second line had two ">>". Just one will cause
:: the SCRIPT.TXT to be created new (if it already
:: exists, it will be erased to start over fresh).
:: The two >> will cause things to be appended to
:: the existing SCRIPT.TXT
:: http://www.ericphelps.com


 

Counting

Incrementing
It isn't real math, but batch files can add one to a number to help you set up loops or generate sequential file names or whatever you want.

Generate a random number
Grab the hundredths digit from the time.
 
 

Network

Find IP and MAC addresses
Collects IP and MAC addresses and user names for all logged in users on the local subnet. I've included a readme file to tell you how to modify it to run on an NT or Novell network. Note: Since the batch files in this package create other temporary batch files and ping lists of IP addresses, some antivirus software identifies it as infected by the firkin, chode, or 911 virus (same thing, different names). The critical difference is that the virus contains destructive code and copies itself onto other machines. My batch files don't do any of those things. NAI/McAfee (which I use) correctly finds no problems. If your antivirus product prevents you from running this code. I'll be happy to work with you to modify it as needed. Remember: These are just plain text batch files you can look at to satisfy yourself of their harmless (but helpful!) nature.

What is my IP address
Three different ways to get your IP address from inside a batch file.

 

To find your IP address in a batch file, you must 
first find your IP address and get it to appear on 
a line:
 
 
Under NT (and some Win9x machines) you can run the 
IPCONFIG program and parse the results:
 
ipconfig.exe | find "IP Address" | find /v " 0.0.0.0"
 
Windows 95 and 98 have a different program 
WINIPCFG. Here's how to do it in Win95:
 
winipcfg.exe /batch %temp%\winipcfg.out
type %temp%\winipcfg.out | find "IP Address" | find /v " 0.0.0.0"
 
Another possibility that works on both types 
of machines is to use ARP:
 
ping.exe -n 1 -i 1 -w 1 www.microsoft.com
arp.exe -a | find "Interface"
 
   If you have NT, let me stop you right here. I do NOT 
   write NT batch code, but I slapped this little bit 
   together. This will put your NT IP address into 
   a file named "ip3.txt":
 
   ipconfig | find "IP Address" > ip1.txt
   for /f "tokens=1-2 delims=:" %i in (ip1.txt) do echo %j> ip2.txt
   for /f "tokens=1-3 delims=." %i in (ip2.txt) do echo %i.%j.%k.21> ip3.txt
   
   You'll find that Win9x solutions will be very moch 
   more complicated because Win9x doesn't have the powerful 
   NT "for" command options.
 
Now, on to those complicated Win9x solutions...
Now that you have the IP on a line, you have to 
extract it from all the other words on the line. 
This is easily done by constructing two other files. 
One of these files is a "line fragment" (a single 
line with NO line termination characters), while 
the other file is a separate batch file. In this case, 
I'll use a batch file named "temp2.bat", so I'll 
need a line fragment that has "call temp2.bat " 
(without the quotes but WITH the trailing space) in 
it. While you could make the fragment ahead of time 
(see http://www.ericphelps.com/batch/lines/frag-man.htm) 
I'll show code here which will make the fragment as 
needed. The fragment will be called "temp.txt":
 
 
:: Make a line fragment
:: See http://www.ericphelps.com/batch/lines/frag-dbg.htm
echo e 100 "call temp2.bat "> script
echo rcx>> script
echo f>> script
echo n temp.txt>> script
echo w>> script
echo q>>script
debug < script > junk
del script
del junk
 
Assuming we have the fragment, we need to put the IP 
data on the end of the fragment and rename it as a batch 
file. Using the NT example above (because it's the shortest):
 
copy temp.txt temp1.bat > junk
ipconfig.exe | find "IP Address" | find /v " 0.0.0.0" >> temp1.bat
 
I redirect the output of commands whose outputs I don't 
want to see into "junk". Under Win9x, I could have redirected 
into "nul", but that just creates a file under NT. So I opt 
for uniformity and let a file named "junk" get created. I'll 
delete it later.
 
Let's recap. Right now we have one pice of code that makes 
the fragment. Another piece puts the IP on the fragment as 
"temp1.bat". So let's peek at what is typically in "temp1.bat":
 
call temp2.bat IP Address. . . . . . . . . : 147.132.1.151
 
Obviously, when we run temp1.bat, it's going to try to call 
something called temp2.bat. And it will pass the rest of the 
line as arguments. The first argument is "IP". the second is 
"Address.", all the way to the twelfth argument which is 
"147.132.1.151". So our temp2.bat will be in the unique 
position of being able to isolate the IP address. 
Unfortunately, we can't access variables higher than 9, so 
we'll have to shift three times to move 12 down to 9. 
Here's what could be in temp2.bat:
 
shift
shift
shift
set IP=%9
 
We can either pre-build our temp2.bat, or we can create it 
like this:
 
echo shift> temp2.bat
echo shift>> temp2.bat
echo shift>> temp2.bat
echo set %%9>> temp2.bat
 
If we pre-build everything, we'll need the main batch file, 
the fragment (temp.txt), and the working file (temp2.bat). 
But I think it's simpler to go for a single file solution. 
Here is all the above sample code together in one file:
 
@echo off
:: Make a line fragment "temp.txt"
echo e 100 "call temp2.bat "> script
echo rcx>> script
echo f>> script
echo n temp.txt>> script
echo w>> script
echo q>>script
debug < script > junk
del script
:: Make the working file "temp2.bat"
echo shift> temp2.bat
echo shift>> temp2.bat
echo shift>> temp2.bat
echo set IP=%%9>> temp2.bat
:: Run the command that finds the IP and create "temp1.bat"
copy temp.txt temp1.bat > junk
ipconfig.exe | find "IP Address" | find /v " 0.0.0.0" >> temp1.bat
:: Run the temp1.bat, which runs temp2.bat, which sets the IP variable
call temp1.bat
:: Remove temporary files
del temp1.bat
del temp2.bat
del temp.txt
del junk
:: Display the result
echo Your IP is %IP%
 
As usual, no guarantee that the above code will work on 
NT because I tested it under Win95. Here's the same batch 
file modified for the Windows 9x "winipcfg" program:
 
@echo off
:: Make a line fragment "temp.txt"
echo e 100 "call temp2.bat "> script
echo rcx>> script
echo f>> script
echo n temp.txt>> script
echo w>> script
echo q>>script
debug < script > junk
del script
:: Make the working file "temp2.bat"
echo shift> temp2.bat
echo shift>> temp2.bat
echo shift>> temp2.bat
echo set IP=%%9>> temp2.bat
:: Run the command that finds the IP and create "temp1.bat"
copy temp.txt temp1.bat > junk
winipcfg.exe /batch %temp%\winipcfg.out
type %temp%\winipcfg.out | find "IP Address" | find /v " 0.0.0.0" >> temp1.bat
:: Run the temp1.bat, which runs temp2.bat, which sets the IP variable
call temp1.bat
:: Remove temporary files
del temp1.bat
del temp2.bat
del temp.txt
del junk
del %temp%\winipcfg.out
:: Display the result
echo Your IP is %IP%
 
As you can see, exactly the same except for the two 
lines needed for winipcfg. Now here it is modified to 
use the ARP program:
 
@echo off
:: Make a line fragment "temp.txt"
echo e 100 "call temp2.bat "> script
echo rcx>> script
echo f>> script
echo n temp.txt>> script
echo w>> script
echo q>>script
debug < script > junk
del script
:: Make the working file "temp2.bat"
echo set IP=%%2> temp2.bat
:: Run the command that finds the IP and create "temp1.bat"
copy temp.txt temp1.bat > junk
ping.exe -n 1 -i 1 -w 1 www.microsoft.com > junk
arp.exe -a | find "Interface" >> temp1.bat
:: Run the temp1.bat, which runs temp2.bat, which sets the IP variable
call temp1.bat
:: Remove temporary files
del temp1.bat
del temp2.bat
del temp.txt
del junk
:: Display the result
echo Your IP is %IP%
 
Finally, you may get an error about being out of 
environment space whenever you try to run a batch 
file that sets variables. The easiest solution is 
to not set the variable! For example, the temp2.bat 
could be modified from this:
 
echo set IP=%%2> temp2.bat
 
to this:
 
echo echo Your IP is %%2> temp2.bat
 
Of course, you'd substitute %%9 for the %%2 I show if 
you used the first two (IPCONFIG or WINIPCFG) 
examples. You also wouldn't need the last two lines 
in the code:
 
:: Display the result
echo Your IP is %IP%
 
because the display would be handled in temp2.bat.

 

What is my Server IP
No, it's not a mind reader. Someone told me they had hundreds of subnets, but the file server in each subnet had an IP address of 21 in every case. He wanted a way to run the same batch file on all workstations at login time that would locate the IP address of the local file server. It had to work on NT and 9x workstations, and it just had to replace the last part of the workstation's dynamic IP with 21. So I wrote this. It is NOT pretty, but that's what compatible code looks like. A pure NT solution can be done in three lines.

@echo off
:: This batch file was built on Win95 and NT, so probably works
:: on everything. Run it with no arguments to see built-in help.
:: It depends heavily on the commands DEBUG, PING, and ARP.
:: It generates lots of temporay files all with names like "~temp"
 
:: Check for argument
if [%1]==[] goto NOARG
goto GOODARG
:NOARG
cls
echo Pass this batch file a number from 1 to 254 and it will set an
echo environment variable named IP with your IP number except with 
echo the number you supplied on the end. For example, if your IP is
echo 127.0.0.1 and you do this:
echo %0 34
echo You'd get the IP variable set to 127.0.0.34
goto DONE
:GOODARG
 
:: Check environment space
set ip=127.000.000.001--------
set pos=00
If [%ip%%pos%]==[127.000.000.001--------00] goto GOODENV
goto BADENV
:BADENV
cls
echo Sorry, but your computer doesn't have enough environment
echo space to hold the variables this program needs to run.
goto DONE
:GOODENV
 
echo e 100 "call ~temp2.bat "> ~temp1.txt
echo rcx>> ~temp1.txt
echo 10>> ~temp1.txt
echo n ~temp1.bat>> ~temp1.txt

 

Ping an entire subnet and find the live computers.
This demonstrates how to increment a number from 1 to 255 and build a loop based on the incremented number.

  This example illustrates addition in batch 
  files. Notice this isn't done by real math, 
  but by text comparisons. A common use for an 
  addition routine would be as a loop counter. 
  Here is a batch file that could ping a 
  subnet looking for computers. Note 
  that this example uses the ADD.BAT shown 
  further below. This example also assumes 
  that PING will deliver a response with the 
  word "Reply" in it ONLY if it succeeds (which 
  is true for Winsock 1 and 2 under Win95):
 
----------------------------------
@echo off
:START
call add.bat
ping 127.0.0.%H%%T%%D% | find "Reply" > nul
if not errorlevel 1 echo %H%%T%%D%
if %H%%T%%D%==254 goto DONE
goto START
:DONE
----------------------------------
 
  Notice that the above example is really a 
  simple loop running from 1 to 254 with the 
  "ping" and "if not" lines inside the loop. 
  By pre-setting values for %H%, %T%, and %D% 
  or by changing the "==254" value, you can 
  easily set the start and ending values of 
  the loop.
 
  If you want a list of the good numbers 
  (instead of just an onscreen display), 
  make another batch file called 
  pinglist.bat with this one line in it:
echo %1 >> goodpings.txt
  and change the line that reads
if not errorlevel 1 echo %H%%T%%D%
  in the example above to
if not errorlevel 1 call pinglist.bat %H%%T%%D%
  Here is the ADD.BAT used in the above example:
 
----------------------------------
:: ADD.BAT
:: Increments a three digit number
:: Works by comparing each digit
:: H=hundreds, T=tens, D=digits
@echo off
if [%H%]==[] set H=0
if [%T%]==[] set T=0
if [%D%]==[] set D=0
:DIGITS
if %D%==9 goto TENS
if %D%==8 set D=9
if %D%==7 set D=8
if %D%==6 set D=7
if %D%==5 set D=6
if %D%==4 set D=5
if %D%==3 set D=4
if %D%==2 set D=3
if %D%==1 set D=2
if %D%==0 set D=1
goto DONE
:TENS
set D=0
if %T%==9 goto HUNDREDS
if %T%==8 set T=9
if %T%==7 set T=8
if %T%==6 set T=7
if %T%==5 set T=6
if %T%==4 set T=5
if %T%==3 set T=4
if %T%==2 set T=3
if %T%==1 set T=2
if %T%==0 set T=1
goto DONE
:HUNDREDS
set T=0
if %H%==9 set H=0
if %H%==8 set H=9
if %H%==7 set H=8
if %H%==6 set H=7
if %H%==5 set H=6
if %H%==4 set H=5
if %H%==3 set H=4
if %H%==2 set H=3
if %H%==1 set H=2
if %H%==0 set H=1
goto DONE
:DONE
----------------------------------
 

Find all FTP servers
Actually just the FTP servers on any small subnet. You build a loop that counts from 1 to 254 and let it script the FTP program. Three batch programs zipped up. You configure and run hunt.bat, it calls the other two.

FTP scripting
The FTP program can be scripted. Assuming you know how to type FTP commands, this tells you that little bit more you need to generate a script -- or turn your script into a batch file. Now you can set up FTP script batch files to automate your downloads.

Wait for your friend's dynamic web site
to come up. Keep pinging it until it responds. Then launch whatever you want. Automatically.

 

  This batch file will repeatedly ping a site until it 
  comes up. You can select how often it will be pinged 
  by changing the number on the 
  choice /c:pq /n /t:p,10 > nul 
  line. Right now I have it set for a 10-second delay. 
  Right now I also have it set to ping 
  "ftp.netscape.com", and you will want to change that 
  to something like MyFriend.dyn.ml.org". Finally, I 
  set it to run NOTEPAD after the ping proves good. 
  You'll want to set it to launch something more useful!
  If you aren't using Winsock2, you may want to update 
  your PING command so it won't hang you. See
  ftp://ftp.microsoft.com/Softlib/MSLFILES/pingupd.exe
 
--------------
@echo off
echo Press "q" to quit.
:START
choice /c:pq /n /t:p,10 > nul
if errorlevel 2 goto DONE
::   Replace "ftp.netscape.com" with whatever
ping ftp.netscape.com | find "timed out" > nul
if not errorlevel 1 goto START
::   Put whatever command you want to run 
::   AFTER the site comes up here. I use Notepad
::   just as an example.
cls
notepad
:DONE
--------------
 
  FYI, the CHOICE command is not generally available under NT. 
  You can get it on the disk version of the Resource Kit, but
  not on the download version. If you really want to implement
  this Win9x solution on NT, you can always steal a copy of 
  CHOICE.EXE from a Win9x box. All reports I've heard say it
  works just fine.

 

Enter your password automatically 
so you can login or attach to file servers at work.
 
I want to automate my login, but it always
stops at the part where I have to enter my 
password.
 
The solution to this depends on how your 
login command works. First try typing 
login /? 
and see if it is possible to specify the 
password on the command line. No decent 
system should allow it! 
 
Next, try creating a file with EXACTLY the 
keystrokes you need to enter in order to 
log in. On my system (using Netware 4), we 
can specify server and user name on the 
command line. All I have to do is enter 
my password and hit "Enter". So the first 
thing I do is create a file named "login.txt" 
with nothing but my password and a carriage 
return:
 
---------
PaSsWoRd
 
---------
 
Then I create the batch file which logs me in. 
It "pipes" the login.txt file into the actual 
login command. Basically, this forces the login 
command to take it's input from the login.txt 
file INSTEAD of from the keyboard:
 
---------
type login.txt | login sanwfs1/ericp
---------
 
In the above example, sanwfs1 is my server, 
and ericp is my user name. Now, if something 
goes wrong with your login (say it tells you 
your password expired and asks you for a new 
one), you are screwed. Nothing you do to the 
keyboard will work because the login command 
ONLY looks at what you've typed into the 
login.txt file. So you better type everything 
you think you MIGHT need in there. You might 
want to put a couple "N" characters in there 
to answer "No" to any questions. Frankly, no 
decent system should allow you to automate 
your login! Any GOOD system will AT A MINIMUM 
clear the keyboard buffer and flush STDIN to 
force password entry from the actual keyboard. 
 
Because your problem won't be as easy to solve 
as the above suggestion, you might also want to 
take a look at the "sendkeys.txt" file in this 
same directory for more suggestions.

 

Windows

Get the user name
Shows how to grab the current Windows or network user name out of the system registry.

     I was messing around with REGEDIT and managed to 
     find the DOS command-line options. I've always 
     wanted to get the user name of the person logged 
     in, so I thought I'd see what I could come up 
     with. There may be easier ways to do it, but I 
     couldn't think of any. Here's the REGEDIT options:
     
     REGEDIT [/L:system] [/R:user] filename1
     REGEDIT [/L:system] [/R:user] /C filename2
     REGEDIT [/L:system] [/R:user] /E filename3 [regpath1]
     REGEDIT [/L:system] [/R:user] /D regpath2
       /L:system       Specifies the location of the SYSTEM.DAT file.
       /R:user         Specifies the location of the USER.DAT file.
       filename1       Specifies the file(s) to import into the registry.
       /C filename2    Specifies the file to create the registry from.
       /E filename3    Specifies the file to export the registry to.
       regpath1        Specifies the starting registry key to export from.
                       (Defaults to exporting the entire registry).
       /D regpath2     Specifies the registry key to delete.
       /S              Specifies "silent" operation when importing
                       (No "Succesfully Imported" window will appear)
 
     Here's a batch file that gets the name of the 
     person logged in to Windows. Note that the name 
     of this batch file CAN NOT be called "Current User.bat":
 
-------------------------------------------
@echo off
start /w regedit /e reg.txt HKEY_LOCAL_MACHINE\System\CurrentControlSet\control
type reg.txt | find "Current User" > "Current#User.bat"
echo set CurrentUser=%%1>"Current User.bat"
call "Current#User.bat"
del "Current?User.bat" > nul
del reg.txt > nul
echo %CurrentUser%
exit
-------------------------------------------
 
     Here's a batch file that gets the name of the 
     person logged in to the network. Note that the 
     name of this batch file CAN NOT be called "username.bat":
 
-------------------------------------------
@echo off
start /w regedit /e reg.txt HKEY_LOCAL_MACHINE\Network\Logon
type reg.txt | find "username" > "us#rname.bat"
echo set NetUser=%%1>"username.bat"
call "us#rname.bat"
del "us?rname.bat" > nul
del reg.txt > nul
echo %NetUser%
exit
-------------------------------------------
 
     Iain Hamilton from Scotland noticed that the 
     returned data comes complete with quotes 
     around it. The way I used the data, quotes 
     weren't a problem, but I've got to admit a 
     general solution should provide the data 
     without quotes. Iain removed the quotes by 
     creating a file named after the returned data. 
     Pretty clever! Here's Iain's suggestion (with 
     his permission):
 
     *******************************************
     *******************************************
     *******************************************
 
     For example for a user Bert, the batch program 
     give you an output of currentuser="Bert", which 
     is all very nice but you can't map shares to a 
     name in quotes.  The following code below is my 
     method of converting 
     currentuser="Bert"
     into
     currentuser=Bert
-------------------------------------------
md temp1
cd temp1
:: creates a temp1 directory for the batch file to work in.  Essentially
:: just a directory that doesn't contain any files.
:: HERES THE IMPORTANT BIT:-
echo hello>%username%
:: echos hello into a file called "Bert"
:: DOS can't call the file "bert", so it drops the quotes on each side....
:: ...giving you a directory with one file in it  (called bert)
dir /B >c:\workingdirectory\user.txt
:: outputs the data to a file called user.txt in the working directory
cd..
copy fragment.txt+user.txt runme.bat
:: merges your pre-prepared fragment containing the text:  
:: set currentuser=
:: with the user.txt into runme.bat
:: runme.bat now contains the string: set currentuser=bert
call runme.bat
:: executes the batch file
deltree temp1
:: just a quick tidy up.
-------------------------------------------

 

Get the user name
Parse the output of the NET command and use the first word as the name of a batch file.

Get the user name
Parse the output of the NET command, but use DEBUG instead of relying on the first word.

Get the computer name
Parse the output of the NET command and use DEBUG to strip the leading // from the result.

@echo off
:: Parses the output of the "net config" command to get
:: the computer name. Uses debug to trim
:: away all unwanted info from the response line.
set value=
:: Use net config to get lots of data, then filter it
net config | find "Computer name" > setvalue.bat
:: Use DEBUG to overwrite the beginning data
>  script echo e 0100 "                       set value="
>> script echo w
>> script echo q
debug setvalue.bat < script > nul
del script
call setvalue.bat
del setvalue.bat
echo Computer name is %value%
 
:: Miles Fenton in the UK pointed out that while 
:: "net config" works under Win9x, you'll need to 
:: use "net config server" or 
:: "net config workstation" when running under NT.

 

 

Track when anybody logs in
How to get a program to run when Windows 9x starts or a new user logs in.

    Your problem is finding a way for a logging program 
    to run anytime someone logs in. If you only want it 
    to run when the computer is rebooted, you could 
    reference your batch file in AUTOEXEC.BAT or 
    WINSTART.BAT. If you only want it to run when Windows 
    starts, you might add it to the "run=" line in WIN.INI 
    or put a shortcut to your batch file in the Startup 
    folder for every user. But if you want it to run for 
    every user (including new ones) at every login, you're 
    stuck going into the registry. Press the "Start" 
    button, select "Run...", type in regedit.exe, and 
    navigate your way to 
    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
    Select "Edit" / "New" / "String Value". Name it 
    whatever you want, and add the name of your batch 
    file for the value data.
    
    Now then... Here is the batch file I use for similar 
    things. I call it BOOTLOG.BAT
 
----------------------------------------------------
call current.bat
echo Login occured at %date% %time% >> c:\bootlog.log
----------------------------------------------------
 
    The call to CURRENT.BAT is used to set the time and 
    date into the environment. Here is what I use for CURRENT.BAT:
 
----------------------------------------------------
:: This batch file MUST be named CURRENT.BAT
:: DATE is %4, TIME is %3
:: Current date is Thu 01-22-1998
:: Current time is  8:52:28.07a
if "%3"=="" goto GETDATE
if "%4"=="" goto SETTIME
goto SETDATE
:SETTIME
set TIME=%3
goto DONE
:SETDATE
set DATE=%4
goto DONE
:GETDATE
echo.|date>temp.bat
call temp.bat
echo.|time>temp.bat
call temp.bat
if exist temp.bat del temp.bat
goto DONE
:DONE
----------------------------------------------------
 
    Someone asked me about combining my batch
    files that identify the user with this batch 
    file. Seemed like a natural! Then I started 
    thinking that all of these batch files create 
    environment variables. And some machines just 
    aren't set up to allow any more variables. So 
    here's code that will log the date and time, 
    will also get the user name three separate 
    ways, and does it all with no variables:
 
----------------------------------------------------
@echo off
echo ****************************** >> C:\login.log
echo. | date | find "Current" >> C:\login.log
echo. | time | find "Current" >> C:\login.log
net config | find "User" >> C:\l

 

Is Windows Running
Deciphering the output of the MEM command to tell a batch file what kind of an environment it is running in

How to shut down a Windows program
This is actually a generic "How to control another app" question. It isn't DOS, but it can be done.

Many times you will have an automated process running that 
you want shut down at a certain time of day. 
 
Try this:
http://www.sysinternals.com/ntw2k/freeware/pstools.shtml
The SysInternals web site has loads of incredible free 
utilities. I don't know how they do it.
 
Also, check out the Resource Kit for your OS. For example, 
the Windows 2000 Resource Kit has a program called PTREE. 
Unfortunately, you have to buy the RK (it isn't available 
as a free download). Find out more about the W2K-RK here:
http://www.microsoft.com/windows2000/techinfo/reskit/rktour/server/S_tools.asp
 
If you don't have Windows 2000, you might investigate what 
utilities are available as part of the resource kit or are 
in the "Zero Administration" kit:
http://www.microsoft.com/ntserver/nts/downloads/recommended/ntkit/default.asp
http://www.microsoft.com/windows98/downloads/contents/WURecommended/S_WUFeatured/W98ZAK/Default.asp
 
PC Magazine wrote a nice utility called "EndItAll 2". 
It's free and it will allow you batch control shutting 
down any program. Search www.pcmag.com for the term 
"EndItAll" or try these URLs:
http://www.pcmag.com/article/0,2997,a%253D13656,00.asp
http://www.pcmag.com/search_results/0%2C3015%2C%2C00.asp?query=EndItAll&selected_site=PC+Magazine
 
 
Finally, if none of the above pre-built solutions are 
available, you can use Windows Scripting. If you don't have 
it, it's available as a free download. See 
http://www.microsoft.com/scripting
Here is a simple VBS script that will shut down Notepad:
 
Dim wsh
Set wsh = CreateObject("WScript.Shell")
wsh.AppActivate "Notepad"
wsh.SendKeys "%{F4}"
 
All you have to do is change the "Notepad" above to match 
the title bar of your program. Alternatively, you can 
match just the beginning or end of the title bar.
 
 
 
Finally, the time of day problem. Your basic approach here 
is to schedule a program to run at a certain time of day. 
This scheduled program will then kill your other program.
 
Frankly, you might want to read the Windows help on the 
"System Agent" (for Win9x), the "AT" command (a 
command-line program in NT, 2K, and XP), or 
"Scheduled Tasks" (GUI application in NT, 2K, and XP).  
McAfee and Norton Antivirus both include a scheduler 
as well. Finally (a last resort!), I wrote a scheduling 
program:
http://www.ericphelps.com/schedule/index.htm

 

Running a batch file after Windows shuts down.
It's a great time to run last-minute tests or delete temporary files.

FAQ: How do I get a batch file to run when Windows 
shuts down? I already know how to get a batch file 
to run when Windows starts (put it in AUTOEXEC.BAT 
or the STARTUP folder).
 
Answer: The trick is to stop Windows from starting 
automatically. If you remember your old Windows 3.1 
days, you remember having to put WIN in your 
AUTOEXEC.BAT to start Windows. Any batch file you 
put after the WIN would be run AFTER Windows 
finished. Amazingly, the same trick works in Windows 
95. The only hard part is stopping Win95 from 
launching automatically. What you need to do is edit 
your C:\MSDOS.SYS file to include BootGUI=0 in the 
Options section:
[Options]
BootGUI=0
The only hard part is that MSDOS.SYS is a hidden, 
system, read-only file. You'll need to remove these 
attributes before you can edit it, then restore the 
attributes after you finish. Or you can download 
Microsoft's great "TweakUI" power toy. Try looking 
in one of these spots:
http://www.microsoft.com/windows/downloads/contents/PowerToys/W95TweakUI/default.asp
http://www.microsoft.com/windows/downloads/bin/W95tweakui.exe
On TweakUI's "Boot" tab, uncheck the "Start UI 
automatically" box.
Whether you use TweakUI or edit MSDOS.SYS manually, 
you'll now be able to add WIN and your shutdown batch 
file to your AUTOEXEC.BAT.

 

Exit windows and shut down your computer
from a batch file? Rundll.exe can do the impossible.

Want to know how to make Windows shut down from a batch 
file? Try reading this nicely written page:
http://www.winguides.com/registry/display.php/900/
 
In addition to the methods described there, I've 
found the below items as well:
 
rundll user,exitwindows 0
For those of you who are more stealthy, 
you don't have to advertise the function name:
 
rundll user,#7 0
 
To do a restart, try one of these:
 
rundll user,exitwindowsexec
rundll user,#246
 
And then there are variations like using rundll32 instead
of rundll:
 
rundll32 user.exe,#7
 
One of the main hangups is in actually trying to shut 
Windows down from a batch file. The problem is that the
DOS session is still running when Windows tries to shut
down. So you'll often get a box telling you to you have
to shut the program (the DOS box) down. The whole purpose
of the "arguments" I show for "exitwindows" is to force 
the shutdown. I've had a lot more success by using a
scheduling program to execute the rundll program.
 
Another problem I ran into was screensavers. Some 
screensavers are considered programs and will stop a 
scheduled reboot. The one that bit me was a screensaver 
from the movie Matrix. All day long I tested my reboot 
just fine, but my scheduled 3am reboot always failed until 
I changed the screensaver.
 
Now then... If you have NT or newer, expect the above 
data to NOT help you! One option is to get hold of the 
resource kit for your version of Windows and look for a 
file called "shutdown.exe" or "logoff.exe":
http://www.microsoft.com/ntserver/nts/downloads/recommended/ntkit/default.asp
http://www.microsoft.com/windows2000/techinfo/reskit/rktour/server/S_tools.asp
 
Another option is a shutdown program I wrote that can be 
used as a screensaver or (if you rename it with an EXE file 
extension) as a standalone program. It works on Win9x, but 
I've been too lazy to walk across the house to test it on 
NT. Get it here:
http://www.ericphelps.com/shutdown/index.htm
 

 

Print multiple files or multiple copies
Even though it may be a Windows program that does the printing, you can automate the process with a batch file.

You have lots of documents you want printed, but you 
don't want to sit there and do each one separately? 
Maybe you don't even want to open all of them at once? 
Well, most word processing programs will print the file 
passed to them if you use the /p option. If you use START 
with the /w option, only one file will be done at a time. 
If you use START's /m option as well, your word processor 
will stay minimized so you don't have to watch it (much). 
In the example below, I assume you want to print all TXT 
and BAT files in the current directory, and that you'll 
use NOTEPAD to do the printing. NOTEPAD is nice because by
default it prints filenames at the top of each page and 
page numbers at the bottom. 
 
for %%x in (*.txt *.bat) do start /w /m notepad.exe /p "%%x" 
 
If you want to change the file name that gets printed, type:
lfnfor off
before you run it and you'll get short file names. Type:
lfnfor on
to get long file names.
 
If you want to print 10 copies, Make a separate batch file 
to call the first one:
for %%x in (1 2 3 4 5 6 7 8 9 10) do call printall.bat

 

How to make a DOS window close
after your batch program finishes. It only takes two lines of code!

FAQ: How do I get my batch file to close it's DOS 
window after it finishes?
 
Answer: Clear the screen! Just add the following 
two lines to the end of your batch file:
@echo off
cls
 
It actually makes sense if you think about it. If 
there is something on the screen, DOS will hold 
the window open so you can see the results. But if 
there is NOTHING on the screen, there is no reason 
to keep the window open!
 
---- Now, before you go and tell me I'm wrong ----
 
The "official" way (everybody keeps reminding me) 
to close a DOS session is to use the EXIT command. 
But this just closes the DOS session, not 
*necessarily* the window! Even Microsoft admitted 
this in their knowledge base. One problem with 
using EXIT is that if you make DOS batch "utilities" 
that you call from other batch files, an exit 
command can cause your entire program to end without 
returning to the original calling batch file. A 
CLS command, on the other hand, is "Mostly Harmless".
 
So I keep things separated. If I want to force a 
DOS session to end, I use EXIT. If I just want 
to allow a finished DOS session to close it's 
window, I use CLS. If I want to be safe, I use 
both.
 
For most people, setting the properties 
for the default command prompt (or creating a 
separate PIF file for each batch file) to close 
the window when completed will solve the problem. 
 
If you're like me and write batch files that 
are expected to work on more than just *your* 
computer, you'll want to remember to clear the 
screen. It's easier to write good complete code 
than it is to get called out to visit half the 
PCs in your organization to set up PIFs.

 

How to make a DOS window run minimized or be invisible 
Using a PIF file, the START command, or Windows Scripting.
 
A fairly common question I get is how to make a batch
file run minimized or invisibly.
 
One way is to right-click the batch file and set it's 
properties. You can select it to run in a minimized 
window and to close when finished. The act of setting 
properties will create a separate "pif" file. From then 
on, any time you run the batch file, the settings will 
be honored. 
 
Another way is to start the batch file with the START 
command using the /MIN option. This has other 
interesting consequences... The batch file will run as 
a separate process. You may also need to use the /WAIT 
option if you need to synchronize the batch file with 
something that has to happen after the batch file. Type 
"start /?" at a DOS prompt for more info. The NT/XP/2000 
START command has lots of cool options, while the 95/98 
command has little more than I've described. Keep in 
mind that the START command will have it's own DOS 
window! So running your batch file with "/min" but 
without "/wait" will cause the START command to very 
briefly pop up a DOS window, then be replaced by your 
batch file running minimized.
 
The coolest way is to make the batch file run totally 
invisibly. This is just a little dangerous because your 
batch file MUST be able to close itself and MUST never
produce an error which might require user input. 
Otherwise the batch file will hang invisibly until 
system shutdown or until someone kills it with the task 
manager. To do this trick REQUIRES that you have Windows 
Scripting installed. Scripting is standard on Win98 and 
newer, and is an optional free download for Win95. To 
test, type "wscript" in the Start/Run dialog. If you get 
a settings dialog, you have it. If you get an error or 
Windows offers to find it for you, you don't have it. 
See my scripting web page for more info:
http://www.ericphelps.com/scripting/index.htm
Save this one line of text as "invisible.vbs":
CreateObject("Wscript.Shell").Run """" & WScript.Arguments(0) & """", 0, False
To run any program or batch file invisibly, use it like this:
wscript.exe "C:\Wherever\invisible.vbs" "C:\Some Other Place\MyBatchFile.bat"

 

 

Disks

Is there a disk in the A: drive?
Using the /f option of command.com allows you to find out without crashing your batch program.

Find the CDROM
Not really. Actually, how to search all drives to find which one has a certain file.

How to format a disk automatically
Just about the worst thing you would want to automate, but it's your life.

Find Disk Size
Use the DEBUG command to trim the output of the CHKDSK command to get disk size.

Find Disk free space
Use the DEBUG command to trim the output of the DIR command to get the free space.

How to open the CD drawer
I didn't figure this one out, and I have no real use for it, but it's just too cool! Not for NT because, well, because.
 

Directories

Save the current directory
Done without using the environment. Put this "pushpath.bat" file in your path. It will create the matching "poppath.bat". You run pusshpath first, then do something that changes your current directory, then run poppath to reset the directory to where you started.

@echo off
:: Create a fragment containing "cd ". Code from
:: http://www.ericphelps.com/batch/lines/frag-dbg.htm
:: The script is in the TEMP directory, but the 
:: fragment will be in the WINDOWS directory so we
:: know it will be in the "path".
echo e 100 "cd "> %temp%\script.txt
echo rcx>> %temp%\script.txt
echo 3>> %temp%\script.txt
echo n %windir%\poppath.bat>> %temp%\script.txt
echo w>> %temp%\script.txt
echo q>>%temp%\script.txt
debug < %temp%\script.txt > nul
del %temp%\script.txt
:: Now finish off the batch file by appending the current directory
truename | find ":" >> %windir%\poppath.bat

 

Run a command in every subdirectory
Automatically change to each subdirectory on your drive (or part of it) and run whatever command you want.

@echo off
:: This batch file will compile a list of all directories,
:: change into each of the subdirectories, then run any
:: command you pass it. If you don't pass it a command, it
:: will just tell you the short name of the directories.
 
:: Check if I can see myself. Needed so I can change
:: back to my home directory. You should ALWAYS use full
:: path and file name (like c:\utils\alldirs.bat) when
:: running this program. Or you can double-click it or
:: do drag'n'drops on it.
if not exist %0 goto DONE    
:: Change to my home directory if I'm not there already
%0\
cd %0\..
 
:: Make a line fragment
:: See http://www.ericphelps.com/batch/lines/frag-dbg.htm
echo e 100 "set dir="> script
echo rcx>> script
echo 8>> script
echo n setdir.txt>> script
echo w>> script
echo q>>script
debug < script > nul
del script
 
:: Now make a list of directories
:: ************** Change the "\" to whatever you want ***********
dir \ /ad /b /s | find /v ":\RECYCLED" > directories.txt
 
:: Now process each line
:LOOPSTART
    rem Check to see if directories.txt is zero bytes
    dir directories.txt | find "directories.txt" | find " 0 " > nul
    if not errorlevel 1 goto DONE
    copy /b setdir.txt + directories.txt directories.bat > nul
    rem Create a new directories.txt minus the first line
    type directories.bat | find /v "set dir=" > directories.txt
    rem Trim the bat file so it only has the first line
    type directories.bat | find "set dir=" > directories.bat
    call directories.bat
    rem Change to the indicated drive and directory
    "%dir%\"
    cd "%dir%"
    rem Run the desired program or TRUENAME if no program
    if [%1]==[] truename
    if not [%1]==[] %1 %2 %3 %4 %5 %6 %7 %8 %9
    rem Change back to my home directory
    %0\
    cd %0\..
goto LOOPSTART    
:DONE
 
:: Clean up
del setdir.txt > nul
del directories.txt > nul
del directories.bat > nul

 

Find the directory Java is in
People who write Java aren't usually batch programmers too! I've seen too many hard-coded batch files that fail unless Java is installed in specific locations. This batch file will read the registry to locate the most current Java runtime directory, then converts that into a short path. You could use that path info to run Java directly or to just set a PATH statement. Works under WinNT/2000, and fails gracefully under Win9x.

@echo off
::This batch file only tested under Windows 2000
::It will detect the short path of the current version 
::of the Java runtime executable
 
::First test to see if we are on NT or similar OS by seeing 
::if the ampersand is interpreted as a command separator
> reg1.txt echo 1234&rem
type reg1.txt | find "rem"
if not errorlevel 1 goto WIN9X
 
::Find the current (most recent) Java version
start /w regedit /e reg1.txt "HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment"
type reg1.txt | find "CurrentVersion" > reg2.txt
if errorlevel 1 goto ERROR
for /f "tokens=2 delims==" %%x in (reg2.txt) do set JavaTemp=%%~x
if errorlevel 1 goto ERROR
echo Java Version = %JavaTemp%
del reg1.txt
del reg2.txt
 
::Get the home directory of the most recent Java
start /w regedit /e reg1.txt "HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\%JavaTemp%"
type reg1.txt | find "JavaHome" > reg2.txt
if errorlevel 1 goto ERROR
for /f "tokens=2 delims==" %%x in (reg2.txt) do set JavaTemp=%%~x
if errorlevel 1 goto ERROR
echo Java home path (per registry) = %JavaTemp%
del reg1.txt
del reg2.txt
 
::Convert double backslashes to single backslashes
set JavaHome=
:WHILE
  if "%JavaTemp%"=="" goto WEND
  if not "%JavaHome%"=="" set JavaHome=%JavaHome%\
  for /f "delims=\" %%x in ("%JavaTemp%") do set JavaHome=%JavaHome%%%x
  for /f "tokens=1,* delims=\" %%x in ("%JavaTemp%") do set JavaTemp=%%y
  goto WHILE
:WEND
set JavaTemp=
echo Java home path (long, with spaces) = %JavaHome%
 
::Convert long path (with spaces) into a short path
for %%x in ("%JavaHome%") do set JavaHome=%%~dpsx
echo Java home path (short path, no spaces) = %JavaHome%
 
::Test the java path to see if there really is a java.exe
if not exist %JavaHome%\bin\java.exe goto ERROR
 
::Make changes to the PATH
echo Insert code here that needs to know the short path to Java.
set path=%JavaHome%\bin;%path%
goto DONE
 
:WIN9X
echo Insert code here for Windows 9x
goto DONE
 
:ERROR
echo Insert code here for conditions where Java.exe can't be found
goto DONE
 
:DONE

 

 

Find the directory your batch file is in
Again for Java programmers who put their batch files in the same directory as their JAR files, but need to know the short path to that directory so they can set their CLASSPATH environment variable. This one works under both Win9X or NT/2000.

@echo off
 
::First test to see if we are on NT or similar OS by seeing 
::if the ampersand is interpreted as a command separator
> script echo 1234&rem
type script | find "rem"
if not errorlevel 1 goto WIN9X
 
:NT
 
echo Running under NT
del script
::Get the current batch file's short path
for %%x in (%0) do set BatchPath=%%~dpsx
for %%x in (%BatchPath%) do set BatchPath=%%~dpsx
echo BatchPath = %BatchPath%
 
:WIN9X
echo Running under Win9X
::An assumption is made that the batch file is run by double-clicking.
::This means %0 is a short file name and path with no quotes
::Test for quotes by quoting %0
if not exist "%0" goto ERROR
:: Make a line fragment per http://www.ericphelps.com/batch/lines/frag-dbg.htm
echo e 100 "set BatchPath="> script
echo rcx>> script
echo e>> script
echo n ~temp.bat>> script
echo w>> script
echo q>>script
debug < script > nul
del script
::Change to the batch file's drive
%0\
::Change to the batch file's directory
cd %0\..
::Use the TRUENAME command to get the short path
truename | find ":" >> ~temp.bat
call ~temp.bat
del ~temp.bat
set BatchPath=%BatchPath%\
echo BatchPath = %BatchPath%
goto TEST
 
:TEST
echo Insert test code here to verify your JAR or CLASS files exist
::Test the batch path to see if "My.jar" is there.
rem - if not exist %BatchPath%My.jar goto ERROR
::Use the information
echo Insert code here which uses the batch file's short path
rem - set CLASSPATH=.;%BatchPath%My.jar
goto DONE
 
:ERROR
echo Insert error-handling code here
goto DONE
 
:DONE

 

Files

When was a file last accessed?
Use the DEBUG command to trim the output of the DIR /V command to get just the access date.

@echo off
:: This batch file uses DEBUG to help determine
:: the last accessed date of a file. Actually, 
:: you can determine the last access date with 
:: the "DIR /V" command. All this batch file does
:: is extract the last accessed date from the 
:: response line. It turns out the date we want is
:: always in the same character position on the line!
:: Debug is used merely to strip away everything
:: before the data we want and replace it with text
:: that says "set accessed=". Debug is then used 
:: to set trim away everything after the data we want.
 
if [%1]==[] goto HELP
goto RUN
 
:HELP
cls
echo You must supply a file name as an argument.
goto DONE
 
:RUN
set accessed=
dir /v %1 | find ":" | find "-" > ~setacc.bat
>  script echo f 100 l 3a 20
>> script echo e 12f "set accessed="
>> script echo rcx
>> script echo 43
>> script echo w
>> script echo q
debug ~setacc.bat < script > nul
del script
call ~setacc.bat
del ~setacc.bat
echo Your file was accessed on %accessed%
 
:DONE

 

Get the root name of a file
Only works on short names, but if you ever want to dump the file extension, here's a way.

 

When was a file created?
Use the DEBUG command to trim the output of the DIR command to get the file created date.

 

Read and write to INI files
A "pure" batch file solution requiring nothing more than standard DOS. It won't work with complex INI files like WIN.INI, but is instead intended for simple one-section private INI data files. Store all your stuff in an INI file instead of in the environment!

 

Copy the most recent file
When a process automatically generates files, it can really help to have a single file name for the most recently generated file.

    Here's a batch file that will determine 
    the most recent file in a directory and 
    copy it to a file with the same extension 
    but with "recent" as the name. For example, 
    multiple users or processes may generate 
    files with names like REPORT45.HTM, 
    REPORT37B.HTM, etc.. Running this batch 
    file would create a RECENT.HTM which would 
    be a copy of the most recent file. 
 
------------------------------------
@echo off
dir /o-d /a-d *.* | find "-" | find ":" > en#er.bat
fc en#er.bat nul /lb1 /n |date|find " 1: " > en#er.bat
echo copy /y %%5.%%6 recent.%%6 > enter.bat
call en#er.bat
del en?er.bat>nul
cls

------------------------------------

Get file sizes in HEX
How to use DEBUG to retrieve the lower 16 bits (64k) of a file's size. Although it isn't done, I show how to get the upper 16 bits as well.

 

Comparing file sizes
Everybody wants to move big files or delete small ones. How is a batch file to tell? You'll wish I just gave you the answer, but I'm forced to teach you the techniques instead. DOS has no native way of comparing, so we have to find ways of using DOS' sorting abilities to do the job.

 

Long and short file names
Shown are several tricks for converting back-and-forth from long to short names, getting the drive letter, and getting the file extension. This isn't made for reading. Just save it as a BAT file and drop a file on it with Explorer. If it gives you what you want, steal the appropriate code section.

@echo off
if not "%1"=="" goto MAIN
 
:HELP
echo    Drag a Windows 95 file and drop it on this batch file
echo    to see what can be done with the filename.
goto DONE
 
:MAIN
rem the next line changes to the drive of the argument
%1\
rem the next line changes to the directory of the argument
cd %1\..
 
echo  The argument to this batch file:
echo %1
echo.
pause
cls
 
echo  Short directory name (output of TRUENAME):
truename | find ":"
echo.
pause
cls
 
echo  Short file name root (searched output of DIR command):
dir %1 | find "-" | find ":">en#er.bat
fc en#er.bat nul /lb1 /n |date|find " 1: ">en#er.bat
echo echo %%5>enter.bat
call en#er.bat
del en?er.bat>nul
echo.
pause
cls
 
echo  Short file name extension (searched output of DIR command):
dir %1 | find "-" | find ":">en#er.bat
fc en#er.bat nul /lb1 /n |date|find " 1: ">en#er.bat
echo echo %%6>enter.bat
call en#er.bat
del en?er.bat>nul
echo.
pause
cls
 
echo  Long directory name (output of CD):
cd
echo.
pause
cls
 
echo  Long file name (output of DIR /B):
dir /b %1
echo.
pause
cls
 
echo  Long fully-qualified file name (output of DIR /S /B):
dir /s /b %1
echo.
pause
cls
 
echo  Short file name (output of FOR with LFNFOR OFF)
lfnfor off
for %%x in (%1*) do echo %%x
echo.
pause
cls
 
echo  Long file name (output of FOR with LFNFOR ON)
lfnfor on
for %%x in (%1*) do echo %%x
echo.
pause
cls
 
echo  Just the drive letter (Error message from MODE)
mode %1> #nval#d.bat
echo echo %%3 >invalid.bat
call #nval#d.bat
del ?nval?d.bat >nul
echo.
pause
 
:DONE
::  http://www.ericphelps.com
rem following code insures this window will close when done
@echo off
cls
exit

 

How to make a randomly-named file.
Or at least how to get an almost random filename guaranteed not to exist in your TEMP directory.

This batch file will generate almost random file names. 
To do this, it takes advantage of the fact that "piping" 
needs a guaranteed nonexistent filename in order to store 
the piped data. By piping anything into the DIR command 
(which ignores the piped data), we can actually see the 
filename assigned. We can reuse this filename immediately 
(because the pipe is done with it), or use it or part of 
it as random. The MODE command is misused here in order 
to generate a "line fragment" to turn the single line of 
data we get into a batch file.
------------------
@echo off
::  First generate a line fragment
mode = > #nvalid.bat
::  Get a picture of what's in TEMP now
dir %temp% > dir1.txt
::  Now pipe something and look at TEMP again
echo.|dir %temp% > dir2.txt
::  Compare the two DIRs. The difference is the 
::  almost random name assigned by the pipe. Put
::  the results on the end of the fragment
fc /lb1 dir1.txt dir2.txt | find ":" >> #nvalid.bat
::  Now create an "INVALID.BAT" to do out work
echo set TEMPNAME=%%3> invalid.bat
::  Now we ca

 

Rename Files with time and date
Many programs generate files which need to be renamed with unique names. Time and date usually do the trick.

   A common question is how to give files names 
   based on date or time. This is most easily done 
   by placing the date and/or time in the environment 
   and working from there. If we had variables named 
   TIME and DATE, we could rename a generic file like 
   FILE.TXT to something like this:
   07-07-1998_4-56-13-83P-FILE.TXT
   with a line like this (assuming the file name is 
   being passed as %1):
   ren %1 "%date%_%time%-%1"
 
   Getting the date into the environment is easy. 
   Here's an example I call SETDATE.BAT:
 
----------------------------------------
@echo off
echo.|date|find "Current" >cu##ent.bat
echo set date=%%4> current.bat
call cu##ent.bat
del cu??ent.bat > nul
----------------------------------------
 
   Getting the time into the environment is just 
   as easy. Unfortunately, the time has embedded 
   colons in it, and colons aren't legal in file 
   names. Periods are legal, but they limit how 
   you can use the rename command. Removing 
   periods and colons is an character at a 
   time problem handled by misusing the 
   CHOICE command. Here's my suggestion 
   I call SETTIME.BAT for getting time 
   into the environment:
 
----------------------------------------
@echo off
echo.|time|find "Current" >cu##ent.bat
echo set time=%%3> current.bat
call cu##ent.bat
:: At this point the time is in the environment but has 
:: colons and periods in it. Both are bad news. They 
:: will be replaced by harmless dashes. CU??ENT.BAT 
:: are no longer needed and will be overwritten below
echo = | choice /c=%time%= cu##ent.bat > current.bat
echo @echo off> cu##ent.bat
echo set time=>> cu##ent.bat
echo :LOOP>> cu##ent.bat
echo shift>> cu##ent.bat
echo if "%%1"=="]?" goto DONE>> cu##ent.bat
echo if "%%1"==":" goto REPLACE>> cu##ent.bat
echo if "%%1"=="." goto REPLACE>> cu##ent.bat
echo goto KEEP>> cu##ent.bat
echo :REPLACE>> cu##ent.bat
echo set time=%%time%%->> cu##ent.bat
echo goto LOOP>> cu##ent.bat
echo :KEEP>> cu##ent.bat
echo set time=%%time%%%%1>> cu##ent.bat
echo goto LOOP>> cu##ent.bat
echo :DONE>> cu##ent.bat
call current.bat
del cu??ent.bat > nul
----------------------------------------
 
   Bad news if you live in the UK! The date
   command there includes slashes which aren't 
   allowed in file names. What's worse, the 
   method I use (using the CHOICE command) to 
   remove colons from the TIME output won't 
   work: CHOICE can't handle slashes. So here 
   is SETDATE.BAT which will work in the US or 
   UK, but insures the date separator is a dash:
 
----------------------------------------
@echo off
echo.|date|find "Current" >cu##ent.bat
echo set date=%%4> current.bat
:: At this point cu##ent.bat contains a line like
:: Current date is Wed 10-06-1999
:: in the US or
:: Current date is Wed 06/10/1999
:: in the UK. But we must remove UK slashes! They
:: always occur in the same place, so we use DEBUG 
:: to ensure they are harmless US style dashes.
echo e 116 "-" > script
echo e 119 "-" >> script
echo w >> script
echo q >> script
type script | debug cu##ent.bat > nul
del script > nul
call cu##ent.bat
----------------------------------------
 
   I heard the above code can set off a virus 
   warning about "Firkin". If that happens 
   to you, this re-written version does the 
   same thing without causing any problems:
 
----------------------------------------
@echo off
> cuttent.bat echo.|date|find "Current"
> current.bat echo set date=%%4
> script.txt echo e 116 "-"
>> script.txt echo e 119 "-"
>> script.txt echo w
>> script.txt echo q
type script.txt | debug cuttent.bat
del script.txt
call cuttent.bat
del cuttent.bat
del current.bat
cls
----------------------------------------
 
   So as a final example, assume I have a program 
   named PROCESS.BAT which produces a file named 
   FILE.TXT. I run the PROCESS program several 
   times a day, but each run overwrites the old 
   FILE.TXT. To save the file, I must rename it 
   with a unique name. I can do this by adding 
   date and time to the filename:
 
----------------------------------------
call process.bat
call setdate.bat
call settime.bat
ren file.txt "%date%_%time%-file.txt"
set time=
set date=
----------------------------------------
 
   Now instead of running my PROCESS.BAT, I run 
   the code shown above. Notice I use dashes to 
   separate the time, date, and file name. Even 
   though there are no spaces in the example I 
   used, I still put quotes around the name. I 
   shouldn't have to, but sometimes the REN 
   command can be picky. Always quote file names 
   to be on the safe side!
 
   CAUTION: All the above code (like 
   virtually all my code) is for Win9x. 
   If you use NT, please go to this page:
   http://www.ericphelps.com/batch/nt/index.htm
   and look for the links telling you how 
   to set the date into the environment. 
   Alternatively, if you installed scripting 
   (it came out with NT SP4), go to this page:
   http://www.ericphelps.com/scripting/index.htm
   and look for the links to "DateName.vbs" 
   and "TimeDateName.vbs" near the bottom of the page.
 
   FYI, the CHOICE command is not generally available under NT. 
   You can get it on the disk version of the Resource Kit, but
   not on the download version. If you really want to implement
   this Win9x solution on NT, you can always steal a copy of 
   CHOICE.EXE from a Win9x box. All reports I've heard say it
   works just fine.

 

 

Rename all files from one extension to another
Shown here renaming VXE files to EXE by creating a list of files to be changed and changing them one-at-a-time

@echo off
echo Please wait while renaming all VXE files to EXE...
:: Renames all .VXE files to .EXE on the C: drive
:: Code derived from 
:: http://www.ericphelps.com/batch/lists/filelist.htm
 
rem ******************* MAGIC HAPPENS HERE *******************
:: Create the list of vxe files
dir /s /b c:\*.vxe > filelist.txt
rem ******************* MAGIC HAPPENS HERE *******************
:: Create the PROCESS.BAT
echo ren "%%filename%%" "*.exe"> process.bat
rem ******************* MAGIC HAPPENS HERE *******************
 
:: Create the FRAGMENT.TXT
:: See http://www.ericphelps.com/batch/lines/frag-dbg.htm
echo e 100 "set filename="> script
echo rcx>> script
echo d>> script
echo n fragment.txt>> script
echo w>> script
echo q>>script
debug < script > nul
del script
 
:START
copy fragment.txt + filelist.txt temp.txt > nul
type temp.txt | find "set filename=" > temp.bat
echo call process.bat >> temp.bat
call temp.bat
type temp.txt | find /v "set filename=" > filelist.txt
copy filelist.txt nul | find "0" > nul
if errorlevel 1 goto START
 
:: Delete temporary files
del temp.bat
del filelist.txt
del temp.txt
del fragment.txt
del process.bat
cls

 

 

Rename all files from one extension to another
Shown here renaming VXE files to EXE by changing into every subdirectory one-at-a-time and doing a wildcard rename.
 

@echo off
:: This batch file will compile a list of all directories,
:: change into each of the subdirectories, then 
:: rename all vxe files it finds to exe. Yes, there
:: are faster ways to do this. Code derived from:
:: http://www.ericphelps.com/batch/samples/alldirs.bat.txt
:: NOTE: This program uses the environment! You may need to
:: increase the size of your environment for it to work
:: properly. Put a line like this in your C:\Config.sys file:
:: shell=command.com /e:4096 /p
:: and then reboot your computer. No more environment problems!
 
:: Check if I can see myself. Needed so I can change
:: back to my home directory. You should ALWAYS use full
:: path and file name (like c:\utils\vxe2exe.bat) when
:: running this program. Or you can double-click it or
:: do drag'n'drops on it.
if not exist %0 goto DONE    
:: Change to my home directory if I'm not there already
%0\
cd %0\..
 
:: Make a line fragment
:: See http://www.ericphelps.com/batch/lines/frag-dbg.htm
echo e 100 "set dir="> script
echo rcx>> script
echo 8>> script
echo n setdir.txt>> script
echo w>> script
echo q>>script
debug < script > nul
del script
 
:: Now make a list of directories
echo Please wait while searching for and renaming vxe files...
dir c:\ /ad /b /s | find /v ":\RECYCLED" > directories.txt
 
:: Now process each line
:LOOPSTART
    rem Check to see if directories.txt is zero bytes
    dir directories.txt | find "directories.txt" | find " 0 " > nul
    if not errorlevel 1 goto DONE
    copy /b setdir.txt + directories.txt directories.bat > nul
    rem Create a new directories.txt minus the first line
    type directories.bat | find /v "set dir=" > directories.txt
    rem Trim the bat file so it only has the first line
    type directories.bat | find "set dir=" > directories.bat
    call directories.bat
    rem Change to the indicated drive and directory
    "%dir%\"
    cd "%dir%"
    rem ******************* MAGIC HAPPENS HERE *******************
    if exist *.vxe ren "*.vxe" "*.exe"
    rem ******************* MAGIC HAPPENS HERE *******************
    rem Change back to my home directory
    %0\
    cd %0\..
goto LOOPSTART    
:DONE
 
:: Clean up
del setdir.txt > nul
del directories.txt > nul
del directories.bat > nul


 

Sounds

Make a beep
You can misuse the CHOICE command or spin up a quick QBASIC program. No ANSI needed.

 

Play sounds
Annoy the cat. Play all your sounds with just enough delay between sounds to let the cat relax.
 

@echo off
:: AnnoyTheCat.bat
:: Plays every sound you have.
:: Because it uses %0, it must be run via the full
:: filename or by double-clicking it in Explorer.
 
:: If there is no argument, do GETFILES
:: otherwise do PLAY
if "%1"=="" goto GETFILES
goto PLAY
 
:GETFILES
echo   Press Ctrl-C to stop the play sequence.
:: Turn long file name expansion for FOR off.
:: Short file names make things much easier.
lfnfor off
:: Use FOR to get all the filenames. Then CALL this
:: batch file, passing the filename as an argument.
for %%x in (c:\windows\media\*.wav) do call %0 %%x
:: These next 3 lines insure this DOS window closes.
@echo off
cls
exit
goto DONE
 
:PLAY
:: Use CHOICE to effect a time delay between sounds.
choice /t:y,10 > nul
:: Here the sound gets played. START uses /W to "wait"
:: until the sound is done and /M to run the SNDREC32
:: program "minimized".
start /w /m sndrec32.exe /play /close %1
 
:DONE
::  http://www.ericphelps.com


 

Installations

Insure Visual Basic 5 exists on a user's machine
Not just test for it, but download and start the installation program up too! Just stick this code in front of any batch file that needs VB5 runtime.

Insure QBASIC exists on a user's machine
Not just test for it, but download and install it automatically too! Just stick this code in front of any batch file that needs QBASIC.
 
 

Miscellaneous

Is a COM port available
Check to see if a com port is available before proceeding.

Hide your batch file code
What if you have to embed a password or something else you don't want seen in your code?

Registry operations
I always meant to expand my entry on this subject, but never got around to it.  It ought to give you a start.

RunDLL and RunDLL32
Batch programs can do the impossible with these commands. Unfortunately, finding things you can actually do is like looking for easter eggs. The list of things you'd actually ever WANT to do is pretty small. I present my UNFINISHED list of commands. I'll only put out the effort to finish it if I hit another project where I need it.

Put the time into the environment
This batch file shows the trick of using a command output as a command line. In this case the "Current Time..." output of the TIME command is used to call a batch file named "CURRENT.BAT".

Read the output of DOS commands
Use FIND.EXE and CHOICE.COM (if needed) to make decisions based on the output of any DOS command. This isn't the discovery of the year, but it can be a very handy trick to know! How would you use this? Tell if ANSI is in memory. Tell if Windows is running. And all automatically!

    I needed a way to let my AUTOEXEC.BAT detect things like whether or 
not I was connected to a network, whether my ZipDrive was connected, what 
server I was connected to, whether my PCMCIA card was in place... That's 
life as a road warrior. Lots of us have no idea what configuration our 
laptops are going to be in from hour to hour, and I, for one, got tired 
of seeing the unending parade of error messages as this or that driver 
failed to load or the printer in my home office didn't get captured. 
Or any of a dozen other easily forseen problems. Heck, it's only a 
batch file, but there should be a way to fix that...
    I was about to write a separate COM program. And then I saved 
myself the trouble. Take a look at the sample program here which is 
written to see if ANSI is available:
 
mem /d | find "ANSI" > nul
if not errorlevel 1 echo ANSI is in memory
 
    MEM is the command I'm interested in here. You could substitute 
virtually any command you wanted. The /D option generates an 
exhaustive list of everything in conventional memory. The entire list 
is then "piped" into the FIND command for searching. FIND generates a 
DOS error code on exit. If it finds something, the errorlevel is zero.
If it can't find it, the errorlevel is one. If there was a problem, 
the errorlevel is two. By testing for ERRORLEVEL 1, I am actually
testing for errorlevel 1 or greater (a quirk of DOS). By inverting it 
with NOT, I am effectively testing for zero exclusively. 
    If you must test for a specific number of finds (for example, to 
look for the 3 instances of WIN in memory), you'll want to count
things using CHOICE:
 
mem /d | find /c "WIN" | choice /n /c:0123456789 > nul
if errorlevel 4 echo This program is running under Windows
 
 
    FIND uses the /C option to "count" the occurences of the text string 
WIN in the MEM output listing. The text string, again, could be anything 
you wanted. The resulting number of occurences is then piped into CHOICE.
    CHOICE takes the first digit of the number supplied by FIND and 
compares it against the list following the /C: option. If the number is 0, 
CHOICE will return an errorlevel of 1 (since 0 is the first item in the 
list). If the number is 1, CHOICE will return an errorlevel of 2, and so 
on. The /N option insures CHOICE doesn't try to generate a prompt of it's 
own. The redirection of CHOICE into NUL stops the number we are discussing 
from being sent to the screen as well.
    IF tests the ERRORLEVEL and takes the appropriate action. Keep in mind 
that "if errorlevel 4" really means "if errorlevel is greater than or 
equal to 4".
    In this example, if Windows is running, we will FIND 3 instances of 
WIN (data, environment, and program). FIND would pipe the number 3 into 
CHOICE. Since 3 is the fourth item in the /C: list, the resultant 
errorlevel would be 4. The IF test would pass, since 4 is greater than 
or equal to 4. And yes, we would then be notified that Windows was most
definitely running.
    A caution: always write this with the CHOICE /C: option having all
10 numbers (zero through nine), even if you're only expecting a zero 
or a one. Things could change, and any value not in the CHOICE list 
will lock CHOICE up. By including all 10 numbers, you prevent a lockup.
 
    FYI, the CHOICE command is not generally available under NT. 
You can get it on the disk version of the Resource Kit, but
not on the download version. If you really want to implement
this Win9x solution on NT, you can always steal a copy of 
CHOICE.EXE from a Win9x box. All reports I've heard say it
works just fine.
 

 

Subroutines in batch files.
Structured programming is here at last! The batch file simply CALLs itself and passes the name of a label it wants to GOTO. Each label contains the standalone "subroutine" code and exits when finished, returning control to the calling incidence. Sound confusing? Just take a look at the simple code and run it.

@echo off
if not exist %0 goto ERROR
if not "%1"=="" goto %1
 
:MAIN
call %0 ECHOHI
call %0 ECHOBYE
goto DONE
 
:ECHOHI
echo Hi!
goto DONE
 
:ECHOBYE
echo Bye!
goto DONE
 
:ERROR
echo.
echo You must run this batch file with it's full name (including the extension).
echo If you run it from another directory, you must include the path as well. 
echo If you include the path, it should be the "short" path (or at least not 
echo include spaces). Running it by typing "%0" isn't good enough!
echo Double-clicking this batch file in Windows Explorer will work just fine...
echo.
goto DONE
 
:DONE

 

Inner Peace
No, I didn't do this! I helped out with a few tips. It's just as odd as it sounds and implementing it in DOS batch is even odder. If you run it, it will try to improve your "Inner Peace". If you read it, it goes a long way towards being a course in how to write batch files

Shut Down PC

 

Save the following as a file with extension *.vbs

 

Set colOperatingSystems = GetObject("winmgmts:{(Shutdown)}").ExecQuery("Select * from Win32_OperatingSystem")

For Each objOperatingSystem in colOperatingSystems

 ObjOperatingSystem.Win32Shutdown(8)

Next


Links

 

In my opinion, all these links point to pages that are as good as -- or better -- than what I offer. Suggestions for new links are always welcomed.
The Ancient Art of DOS Batch Files
Simon Shepard's ss64.com NT Syntax
Batfiles - The DOS batch file programming handbook
EasyDOS Command Index
Frank-Peter Schultze

1