Batchfiles FAQ
By Leonardo Pignataro (also known as Secret_Doom). Batch scripts for Windows 9x, not NT. All scripts contained in this page were developed and tested under Windows 98 Second Edition (DOS 7.10a). These scripts are likely to work on other DOS based systems, such as previous versions of DOS. However, since there are many differences between NT batch scripting and DOS batch scripting, some of these scripts might not work under NT systems (Windows NT,2K,XP). In fact, I do not recommend you to use any of this code on a NT system unless a note stating otherwise is made. Many scripts use the %temp% variable, which ought to point to a directory for temporary files. All scripts that use such variable assume it is assigned to a valid and writeable path, in short name format (path doesn't contain any spaces, nor commas and semicolons). The batch scripts on this site may be freely modified or altered as necessary to meet your needs. I make no guarantees or warranties, express, implied or of any other kind to this code or any user modifications. Do not use in a production environment until you have tested it throughly in a secured lab environment. Use at your own risk. If you find any bugs related to html, batch or anything else, please report to secret_doom@hotmail.com NEWS
Since the subject is so old, and I moved on to other subjects of study, I no longer update this site. However, hoping that it will still be of use for at least a few of folks in the future, I keep in online. FAQ
How to...
01. Get system's current date into a variable (with separators) 01. Get system's current date into a variable (with separators)
:: Get current date into %date% variable
@echo off
echo @PROMPT SET %%1=$D$_> %temp%.\t1.bat
%comspec% /c %temp%.\t1.bat > %temp%.\t2.bat
call %temp%.\t2.bat DATE
for %%? in (t1.bat t2.bat) do del %temp%.\%%?
echo DATE=%DATE%
02. Get system's current date into a variable without separators - look for the appropriate method for the intended OS.
Variables:
:: Get system's current date into multiple variables
@echo off
if "%1"=="GoTo" goto %2
echo e180 BF 07 01 B4 04 CD 1A E8 02 00 89 CA E8 00 00> %temp%.\t1.src
echo e18F 86 D6 88 D0 D4 10 0D 30 30 86 C4 AB 47 C3>> %temp%.\t1.src
for %%? in (rip 180 g w q) do echo %%?>> %temp%.\t1.src
echo SET %%1=XX XX XX XX> %temp%.\t2.bat
DEBUG %temp%.\t2.bat < %temp%.\t1.src > nul
call %temp%.\t2.bat _
%0 GoTo parse %_% MM DD YC YY
:parse
set %7=%3
shift
if not "%7"=="" goto parse
for %%? in (t1.src t2.bat) do del %temp%.\%%?
set _=
The variables should be used after all the code, below it. So, if you wanted to create a folder
with datestamp on the format DDMMYYYY, you would include the following on the bottom of the code:
md C:\%DD%%MM%%YC%%YY%
for /F "tokens=1-4 delims=/- " %%A in ('date/T') do set DATE=%%B%%C%%D
Variables:%%A = week day (Mon, Tue, etc) %%B = month %%C = day %%D = year (4 digits)
Note 1: those variables are only valid inside the second compound of the FOR cmd
(basically only on that line). That means if you intend to use the date afterwards, not only in
the FOR cmd itself, you must save it on an enviroment variable. When the FOR cmd ends, those
"for-variables" will lose their values, but the environment variable assigned won't. 03. Get system's current time into a variable (with separators)
:: Get current date into %date% variable
@echo off
echo @PROMPT SET %%1=$T$_> %temp%.\t1.bat
%comspec% /c %temp%.\t1.bat > %temp%.\t2.bat
call %temp%.\t2.bat TIME
for %%? in (t1.bat t2.bat) do del %temp%.\%%?
echo TIME=%TIME%
04. Get system's current time into a variable without separators - look for the appropriate method for the intended OS. :: Get current time into variable without separators @echo off echo @PROMPT SET %%1=$T$_> %temp%.\t1.bat %comspec% /c %temp%.\t1.bat > %temp%.\t2.bat FIND "= " < %temp%.\t2.bat > nul if not errorlevel=1 echo e107''30> %temp%.\t1.bat for %%? in (e109''3B e10C''3B e10F''3B w q) do echo %%?>> %temp%.\t1.bat DEBUG %temp%.\t2.bat < %temp%.\t1.bat > nul call %temp%.\t2.bat TIME :: Next line defines time format. The values are: :: %%1=Hours (military time) %%2=Minutes %%3=Seconds %%4=Cents of seconds echo SET TIME=%%1%%2%%3> %temp%.\t1.bat call %temp%.\t1.bat %TIME% for %%? in (t1.bat t2.bat) do del %temp%.\%%? echo TIME=%TIME%
for /F "tokens=1-4 delims=:., " %%a in ('time/T') do set TIME=%%a%%b%%c
Variables:%%a = hours (am/pm) %%b = minutes %%c = AM/PM Note: those variables are only valid inside the second compound of the FOR cmd (basically only on that line). That means if you intend to use the date afterwards, not only in the FOR cmd itself, you must save it on an enviroment variable. When the FOR cmd ends, those "for-variables" will lose their values, but the environment variable assigned won't. You can also get the hours in military time, as well as the second cents. For that, use the NT systems alternate method.
:: Omit following line if intended OS = Windows 2000
set TIME=
for /F "tokens=1-4 delims=:., " %%a in ("%TIME%") do set TIME=%%a%%b%%c
Variables:%%a = hours (military time, e.g.: 21) %%b = minutes %%c = seconds %%d = second cents That command is based on the %TIME% variable, which is set by default on Windows 2000 and XP. Because of that, it will not work on Windows NT. That first line may seem a little weird, since it takes out the value of a variable and then uses such value. But it isn't really taking out the value, it's just reseting it. If a value is given to the TIME variable on an Win2K/XP environment, where the original value for that variable is the current time, it will no more carry it (it will carry the value given, instead). By taking such value out, it comes to carry the current time again. 05. Parse date and time into multiple variables. Variables:
:: Parse the current date and time into multiple variables
@echo off
if "%1"=="GoTo" goto %2
echo @PROMPT SET %%%%1=XX XX XX XX;$T$_> %temp%.\t1.bat
%comspec% /c %temp%.\t1.bat |FIND " "> %temp%.\t2.bat
FIND "= " < %temp%.\t2.bat > nul
if not errorlevel=1 echo e113''30> %temp%.\t1.bat
echo e180 BF 07 01 B4 04 CD 1A E8 02 00 89 CA E8 00 00>> %temp%.\t1.bat
echo e18F 86 D6 88 D0 D4 10 0D 30 30 86 C4 AB 47 C3>> %temp%.\t1.bat
for %%? in (e115''3B e118''3B e11B''3B rip 180 g w q) do echo %%?>> %temp%.\t1.bat
DEBUG %temp%.\t2.bat < %temp%.\t1.bat > nul
call %temp%.\t2.bat _
%0 GoTo parse %_% MO DD YC YY HH MI SS CS
:parse
for %%? in (1 2) do shift
:parse_lp
set %9=%1
shift
if not "%9"=="" goto parse_lp
for %%? in (t1.bat t2.bat) do del %temp%.\%%?
set _=
echo Hours: %HH%
echo Minutes: %MI%
echo Seconds: %SS%
echo Cents of Seconds: %CS%
echo Day: %DD%
echo Month: %MO%
echo Year Century: %YC%
echo Year: %YY%
06. Get user input (and send to %input%) - this alpha version does NOT include error checking for invalid characters (| < > =). For distributing, beta version is recommended. Many people use input methods which are based on DOS language, so they are not multi-language. I think such a thing in a batch script is TOO bad, so this script will work on any language of DOS.
:: Get user input - no error check
@echo off
echo Type input:
FC con nul /lb1 /n |FIND "1:" > %temp%.\t1.bat
echo e102'set %%1='> %temp%.\t2.dat
for %%? in (w q) do echo %%?>> %temp%.\t2.dat
DEBUG %temp%.\t1.bat < %temp%.\t2.dat > nul
call %temp%.\t1.bat INPUT
for %%? in (t1.bat t2.dat) do del %temp%.\%%?
echo INPUT=%INPUT%
07. Get user input (and send to %input%) - this beta version does include error checking for invalid characters (| < > =). In a batch script which requires several inputs from user, I recommend putting the input part on a subroutine (see second script). :: Get user input - includes error check @echo off echo e102'set %%1='> %temp%.\t1.dat for %%? in (w q) do echo %%?>> %temp%.\t1.dat :input echo. echo Type input: FC con nul /lb1 /n |FIND "1:" > %temp%.\t2.bat :: Error checking begins here FIND "|" %temp%.\t2.bat > nul if not errorlevel=1 goto error FIND "=" %temp%.\t2.bat > nul if not errorlevel=1 goto error FIND "<" %temp%.\t2.bat > nul if not errorlevel=1 goto error FIND ">" %temp%.\t2.bat > nul if not errorlevel=1 goto error :: Error checking end DEBUG %temp%.\t2.bat < %temp%.\t1.dat > nul call %temp%.\t2.bat INPUT for %%? in (t1.dat t2.bat) do del %temp%.\%%? echo INPUT=%INPUT% goto eof :error echo. echo @PROMPT Invalid input. Invalid characters: $Q $L $G $B$_> %temp%.\t2.bat %comspec% /c %temp%.\t2.bat |FIND ":" goto input :eof If you need to take several inputs from user, it's not a good thing to repeat the above code those several times on the batch script. Instead, use a subroutine, like below. @echo off if "%1"=="GoTo" goto %2 :: Open a child shell to get more enviroment space. :: This is also good because we don't need to unSET :: variables at the end: %comspec% /e:4096 /c %0 GoTo start goto eof :start :: Temporary files table - use only .bat extentions SET T1=%TEMP%.\TEMP1.BAT SET T2=%TEMP%.\TEMP2.BAT SET F1=%TEMP%.\TEMP3.BAT SET F2=%TEMP%.\TEMP4.BAT :: Create temporary work files echo e102'set %%1='> %F1% for %%? in (w q) do echo %%?>> %F1% echo @PROMPT Invalid input. Invalid characters: $Q $L $G $B$_> %T1% %comspec% /e:4096 /c %T1% > %T2% FIND ":" < %T2% > %F2% echo. echo First name: :: Get user input and save on %FNAME% call %0 GoTo input fname echo. echo Middle name (note that this one may be blank) :: Get user input and save on %MNAME% :: Note that this input may be blank, so the /b switch is used call %0 GoTo input mname /b echo. echo Last name: :: Get user input and save on %LNAME% call %0 GoTo input lname echo. echo Gender: :: Get user input and save on %GENDER% call %0 GoTo input gender :: ECHO the variables. Note that these won't be :: avaliable when the batch script terminates, :: since this is being run on a child shell (when :: the child shell is terminated, the variables :: come back to their original state) echo. echo FNAME=%FNAME% echo MNAME=%MNAME% echo LNAME=%LNAME% echo GENDER=%GENDER% :: Delete temporary files for %%? in (%T1% %T2% %F1% %F2%) do if exist %%? del %%? goto eof :: Subroutine syntax: call %0 GoTo: input varname [/b] :: varname - variable to save input :: /b - lets input be blank :input FC con nul /lb1 /n > %T2% FIND "1:" < %T2% > %T1% FIND "|" < %T1% > nul if not errorlevel=1 goto input_e1 FIND "<" < %T1% > nul if not errorlevel=1 goto input_e1 FIND ">" < %T1% > nul if not errorlevel=1 goto input_e1 FIND "=" < %T1% > nul if not errorlevel=1 goto input_e1 DEBUG %T1% < %F1% > nul call %T1% _ if not "%4"=="/b" if "%_%"=="" goto input_e2 set %3=%_% set _= goto eof :input_e1 type %F2% goto input :input_e2 echo Invalid input. Input may not be blank. goto input :eof 08. Get a random number (00-99) - the random number generated on this script isn't a true random number. It's actually the seconds' cents from current time. :: Get a random number (00-99) :: The number is actually the seconds' cents @echo off echo @PROMPT $T$_> %temp%.\t1.bat %comspec% /c %temp%.\t1.bat > %temp%.\t2.bat echo e104'set %%1='> %temp%.\t1.bat for %%? in (f100L4''20 w q) do echo %%?>> %temp%.\t1.bat DEBUG %temp%.\t2.bat < %temp%.\t1.bat > nul call %temp%.\t2.bat RANDOM for %%? in (t1.bat t2.bat) do del %temp%.\%%? echo RANDOM=%RANDOM% 09. Get CD (Current Directory) into a variable
:: Get Current Directory into %CD%
@echo off
echo.exit|%comspec%/Kprompt set CD=$P$_|FIND " " > %temp%.\t1.bat
for %%? in (call del) do %%? %temp%.\t1.bat
echo CD=%CD%
10. Make a menu - there are several ways of doing a menu. I'll show 3 examples. @echo off echo Choose: echo. echo [1] Full install echo [2] Minimum install echo [3] Quit echo. choice /c:123 if errorlevel=3 goto end if errorlevel=2 goto min goto full @echo off echo Choose user: echo [A]gent Smith echo [N]EO echo [M]orpheus echo [T]rinity echo. choice /c:ANMT if errorlevel=4 echo Hello Trinity! if errorlevel=3 if not errorlevel=4 echo Hello Morpheus! if errorlevel=2 if not errorlevel=3 echo Hello NEO! if not errorlevel=2 Hello Agent Smith!NOTE: "if errorlevel=3" will return a true value if errorlevel is EQUAL OR GREATER than 3, not only if it's equal 3.
@echo off echo Choose user: echo [A]gent Smith echo [N]EO echo [M]orpheus echo [T]rinity echo. choice /c:ANMT if errorlevel=1 set user=Agent Smith if errorlevel=2 set user=NEO if errorlevel=3 set user=Morpheus if errorlevel=4 set user=Trinity echo Hello %user%! set user= 11. Make a loop @echo off :loop echo Loop! goto loopThat is an endless loop (not a good thing). Loops should eventually end. The following script will make 5 loops and then will skip the loop: @echo off set loop= :loop set loop=.%loop% echo Loop! if not "%loop%"=="....." goto loop echo End! 12. Make a counter - both scripts are addapted from an original script by William Allen. @echo off if "%1"=="GoTo" goto %2 :: 000 - 999 version (fixed digit length) :: Set START less than STOP less than or equal to 999 :: You MUST include trailing zeroes on the left, so that :: START and STOP are 3-digit long set START=005 set STOP=137 call %0 GoTo count1 set N= goto eof :count1 if "%STOP%"=="" goto eof if not "%5"=="" goto count2 for %%? in (0 1 2 3 4 5 6 7 8 9) do call %0 GoTo count1 %3 %4 %%? goto eof :count2 set N=%3%4%5 if "%N%"=="%START%" set START= if not "%START%"=="" goto eof :: %N% represents the current number count echo N=%N% if "%N%"=="%STOP%" set STOP= :eof @echo off if "%1"=="GoTo" goto %2 :: 0 - 999 version :: Set START less than STOP less than or equal to 999 :: Do NOT include trailing zeroes on the left set START=24 set STOP=137 call %0 GoTo count1 set N= goto eof :count1 if "%STOP%"=="" goto eof if not "%5"=="" goto count2 for %%? in (0 1 2 3 4 5 6 7 8 9) do call %0 GoTo count1 %3 %4 %%? goto eof :count2 set N=%3%4%5 if "%3"=="0" set N=%4%5 if "%3%4"=="00" set N=%5 if "%N%"=="%START%" set START= if not "%START%"=="" goto eof :: %N% represents the current number count echo N=%N% if "%N%"=="%STOP%" set STOP= :eof 13. Delay a batch file. There's a basic method to make a delay, which is using the CHOICE command with the /T switch. The following line will take a 60 second delay:
CHOICE /c:$ /t:$,60 /n > nul
:: ^^ Time to wait (from 1 to 99)
The delay will be skipped if user enters "$". That is useful if you want to give the option to
skip the delay. It's also possible to make a certain action only if the delay is waited, and do
a different action if it is skipped (exit, for instance). Example:
echo Waiting (type "q" to quit) ... CHOICE /c:$q /t:$,60 /n > nul if errorlevel=2 goto exit echo 60 seconds have passed! :exit echo End of exampleThe message "60 seconds have passed!" will only be shown if the delay is waited. But, in this method, there must be another character other than the one which exits (in this case, 'q') passed with the /c switch, which will be used to identify that the delay was waited (each option returns a different errorlevel, and we need such errorlevels to later know if the delay was waited or skipped). In this case, the "$" char is passed. But there's a little problem with that: what if user enters that char? What happens then is that the delay is skipped but the errorlevel returned is the one which identifies that the delay was waited. So, the logic of the scritp gets screwed, and there's nothing to stop the user from entering such char. However, that's not such a big deal because the user is unlikely to enter the "$" char, since we don't inform him that such char does anything (but it's possible that he enters it). So, to reduce the chance of that happenning, you may choose another character to have an errorlevel which will identify that the delay was not skipped, a character difficult to be typed on the keyboard (and so even more unlikely to be entered by accident). Example:
echo Waiting (type "q" to quit) ...
CHOICE /c:¥q /t:¥,60 /n > nul
:: ^-----^-- odd "delayed exit" character
if errorlevel=2 goto exit
echo 60 seconds have passed!
:exit
echo End of example
You can also deny the option to skip the process. In that case, we may lock the keyboard so
there isn't even the slightest chance of user hitting the character which return an errorlevel
that will inform us that the delay was waited:
type nul|CHOICE /c:$ /t:$,60So far, so good. However, all of the the above methods can only make a delay from 1 to 99 seconds. In order to make a bigger delay, you may: 1. Combine multiple delay lines, like this: type nul|CHOICE /c:$ /t:$,90 /n > nul type nul|CHOICE /c:$ /t:$,90 /n > nulThat would take 3 minutes delay (2 x 90 seconds = 180 seconds = 3 minutes) and will lock the keyboard during that time. 2. Combine a single delay line with one of the scripts from FAQ #12 (which shows how to make a counter). For instance, to take a 2 hours delay (120 x 60 seconds = 120 minutes = 2 hours) use the following script: @echo off if "%1"=="GoTo" goto %2 :: - This delay time has range from 000 to 999 units. :: - The delay time for each unit may be changed further :: on the script, on the line with the CHOICE command. :: - Trailing zeros on the left of this number must be :: included so it's 3 characters long (e.g.: 005). set DELAY=120 set QUIT=0 echo Hit "s" to skip delay. :: Invoke the delay process call %0 GoTo count1 :: After the delay, the value of the variable %QUIT% informs :: whether the delay was waited (quit=0) or skipped (quit=1) if "%QUIT%"=="0" echo Delay waited. if "%QUIT%"=="1" echo Delay skipped. set quit= goto eof :count1 if "%DELAY%"=="" goto eof if not "%5"=="" goto count2 for %%? in (0 1 2 3 4 5 6 7 8 9) do call %0 GoTo count1 %3 %4 %%? goto eof :count2 if "%quit%"=="0" CHOICE /c:¥s /t:¥,60 /n > nul if errorlevel=2 set quit=1 if "%3%4%5"=="%DELAY%" set DELAY= :eof 14. Make changes on the Windows Registry automatically. The methods stated here won't work on NT systems (Windows NT4-,2K,XP). In such systems, use the REG.EXE program, which is pretty much self-explaining (just type "reg /?" on the command prompt and you'll get all the info you need). But on Win9x, such info is not given. First of all, I must say that playing around with the Windows Registry could be very dangerous, so be sure to test anything througly on a secure lab environment before distributing. If something goes wrong and you mess up with your registry, use the command SCANREG to restore it. But you don't want to explain that procedure to any user who had his system screwed up because of some bad code you've written, so be careful. All the information on this section is based on the behavior of the REGEDIT command under Windows 98 Second Edition. If you're using another similiar system, such as Windows 95 or Millennium, be sure to check for any differences on the behavior of the REGEDIT command. Use the following information at your own risk. In order to make changes on the Windows Registry, you may use REGEDIT.EXE, which comes with perhaps all versions of windows, since it's the same program that gives the user a GUI access to the registry. In order to automate the process, along with that application we'll be using scripts (source files) containing the desired changes to the registry. The syntax for using scripts with regedit is: REGEDIT.EXE [/S] filenameYou have to follow that syntax order, with the switches before the script filename. Switches after the filename seem to be ignored. The /S switch is for Silent mode, an undocumented switch. It will supress the prompt asking if user wants to add the information on filename to the registry, as well as any other messages, like error messages or a report of successfulness. All those messages/prompts would appear as windows to the user (if Windows is running). You might find other sites stating that the /C switch should be used to import data to the registry, but you should NOT use it. Though while under Windows it makes no difference wether you use it or not, on DOS real mode using the /C switch will cause the registry to be entirely erased and then only the keys/values specified on the script file will be created. Now all we need to know is how to make such scripts. A file is identified as a valid Win9x registry script by the string "REGEDIT4" (case sensitive) on the very beginning of the file. There may be spaces or tabs before the string, but nothing else. No semicolons, no line breaks, etc. REGEDIT4Key names are enclosed in []'s and are not case sensitive. Example: [HKEY_CLASSES_ROOT\Config]And about values, there are three kinds of values: string values, binary values and DWORD values. Here's an example of the syntax of each one of those kinds, in that order: "MyStringValue"="some text" "MyBinaryValue"=hex:00,a5,f9,55 "MyDWORDValue"=dword:0000ff5aThe value names (to the left of the equals sign) are all case insensitive ("MyString" is the same as "MYSTRING"), but the value type identifiers ("hex:" and "dword:") are case sensitive and must be written in lowercase, otherwise they will be ignored and the entry won't be added/modified. If you specify a value name that already exists, it will be overwritten without prompting. The default value from each key is represented by a @. Example: @="active"Values must have a key associated. On the script, each key name should be written and below it goes the values to be added/changed. Here's an example of a complete valid registry script: REGEDIT4 [HKEY_CLASSES_ROOT\Config\0002] "Interface"="GUI" [HKEY_CURRENT_USER\Hardware] @="" "ProcessorID"=""That script assigns the value "GUI" to the entry "Interface" on the HKEY_CLASSES_ROOT\Config\0002 key. If the key doesn't exist, the whole key structure is created (even if the Config key doesn't exist it will also be created, along with the 0002 key). The exception is the main keys, the first ones on the key structure (the HKEY's). It seems new main keys can't be created, at least not with this syntax. As far as I could find out, you'll have to keep yourself to the existing main keys (HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER, etc). That script from the last example does also take out the values from the default value and the entry "ProcessorID", on the key HKEY_CURRENT_USER\Hardware. It doesn't actually delete the entries, it just deletes their values. The following would really delete the entry "ProcessorID": (...) [HKEY_CURRENT_USER\Hardware] "ProcessorID"=- (...)Note that even for deleting an entry you must specify in which key it is (pretty obvious). The syntax to delete entire keys is [-KEYNAME]. Example: [-HKEY_CURRENT_USER\Hardware]That would delete the entire key, regardless of what's inside it. Pretty much like DELTREE, not like RD. I don't know if it's possible to delete main keys (HKEY's), I didn't feel like trying such thing to find out... That's about it. You probably know everything you need to make a registry script. Embedding it on a batch script source is easy enough. Example: @echo off echo REGEDIT4> %temp%.\regscr.tmp echo [HKEY_CURRENT_USER\Software\Macrosoft]>> %temp%.\regscr.tmp echo "Resignation"="True">> %temp%.\regscr.tmp REGEDIT /S %temp%.\regscr.tmp del %temp%.\regscr.tmpGot it? You can also export keys from the registry. The syntax is: REGEDIT.EXE /E filename [regpath]Regpath would be the key to export. If you don't specify it, the entire registry will be exported (generally a LOT of data). That's all. 15. Parse a string - break a string (variable) into separated characters. :: Parse a string (%str%) @echo off if "%1"=="GoTo" goto %2 :: Set string to parse set str=Cabron echo ;|CHOICE /s /c;%str%; %0 GoTo parse;> %temp%.\t1.bat for %%? in (call del) do %%? %temp%.\t1.bat goto eof :parse if "%3"=="[" shift IF "%3"=="]?" goto eof echo %3 shift goto parse :eofThe output is: C a b r o n :: Parse a string (%str%) @echo off :: Set string to parse set str=some text echo `h}aXP5y`P]4nP_XW(F4(F6(F=(FF)FH(FL(Fe(FR0FTs*}`A?+,> %temp%.\t1.com echo fkOU):G*@Crv,*t$HU[rlf~#IubfRfXf(V#fj}fX4{PY$@fPfZsZ$:K0>> %temp%.\t1.com echo 6$-tH[fHzt'fZJtxP'7:p*_q5b*RJo09Mleb\f7+V(52O-f,`u/\vm(]>> %temp%.\t1.com echo e.iv+y7(-W:ldwn0V.-Z0I6l3)@f2r=?rV+5lYzH]'=v\{)(c\#>> %temp%.\t1.com :loop if "%str%"=="" goto endloop %temp%.\t1.com %str%> %temp%.\t2.bat call %temp%.\t2.bat str c :: Right here, %C% will represent each character :: from the parsed string echo "%c%" goto loop :endloop for %%? in (t1.com t2.bat) do del %temp%.\%%?Output: "a" " " "b" ";" "c" "," "d" "/" "e" 16. Check if a file or directory exists To check for a file existance:
if exist c:\path\file.ext echo FILE FOUND
if not exist c:\path\file.ext echo FILE NOT FOUND
:: OR USE IT LIKE THIS:
if exist c:\path\file.ext goto found
goto notfound
To check for a directory:
if exist c:\folder\nul echo DIRECTORY FOUND if not exist c:\folder\nul echo DIRECTORY NOT FOUND 17. Check if a file contains a string @echo off FIND "string" filename.ext > nul if errorlevel=1 echo NOT FOUND if not errorlevel=1 echo FOUND 18. Compare variables that contain spaces/commas/semicolons. You cannot simply use the IF statement to compare strings and variables that contain spaces or similar characters. If you do the following; if "%MyVar%"=="some text" ...That will not work. It could even display a syntax error message, depending on the value of the variable and on the MS-DOS version. So, what to do? I generally use this work-around:
:: Checking if "%MyVar%"=="some text"
set [some text]=
set [%MyVar%]=$
if "%[some text]%"=="$" echo TRUE
if not "%[some text]%"=="$" echo FALSE
set [%MyVar%]=
That method is pretty weird and counter-intuitive, but it works just fine. You can probably
understand how it works if you look at the code for a while. Imagine a value for %MyVar% and
see where it gets expanded and what it happens then.
19. Replace a string on a file automatically. On this issue, a 3rd party applcation (non-MS program, which will not be present on a MS default system) can be much useful. However, using 3rd party apps on batch files meant for distribution may be undesired, because it is necessary to distribute not only the batch file but also the program, so you need 2 files instead of a single one. So, I'll present two methods - one using a 3rd party application, SED.EXE, and another one which does not require such applications. However, the capabilities of this second one are limited, while the use of the first one is more wide and reliable.
SED.EXE "s/Old String/New String/g" < infile > outfileInfile and and outfile must not be the same. That's just a very basic command from SED, and also the most used. However, it has many other capabilbities. There are lots of tutorials that teach how to use SED more deeply. You can go here (for basic overview) or here (for more detailed and wide tutorial).
@echo off :: Create the assembler program, by Herbert Kleebauer echo Bj@jzh`0X-`/PPPPPPa(DE(DM(DO(Dh(Ls(Lu(LX(LeZRR]EEEUYRX2Dx=> %temp%.\sbs2.com echo 0DxFP,0Xx.t0P,=XtGsB4o@$?PIyU!WvX0GwUY Wv;ovBX2Gv0ExGIuht6>> %temp%.\sbs2.com echo ?@}IKuNWpe~Fpe?FNHlF?wGMECIQqo{Ox{T?kPv@jeoSeIlRFD@{AyEKj@>> %temp%.\sbs2.com echo iqe~1NeAyR?mHAG~BGRgB{~H?o~TsdgCYqe?HR~upkpBG?~slJBCyA?@xA>> %temp%.\sbs2.com echo LZp{xq`Cs?H[C_vHDyB?Hos@QslFA@wQ~~x}viH}`LYNBGyA?@xAB?sUq`>> %temp%.\sbs2.com echo LRy@PwtCYQEuFK@A~BxPtDss@fFqjVmzD@qBEOEenU?`eHHeBCMs?FExep>> %temp%.\sbs2.com echo LHsPBGyA?@xAunjzA}EKNs@CA?wQpQpKLBHv?s`WJ`LRCYyIWMJaejCksl>> %temp%.\sbs2.com echo H[GyFGhHBwHZjjHeoFasuFUJeHeB?OsQH[xeHCPvqFj@oq@eNc?~}Nu??O>> %temp%.\sbs2.com echo ~oEwoAjBKs?Zp`LBzHQzyEFrAWAG{EFrAqAGYwHTECIQ{coKIsaCsf{Oe~>> %temp%.\sbs2.com echo CK}Ayre~CNFA{rAyEKFACrA{EKGAjbA}eKGSjNMtQFtc{OAyDGFj?{FDGQ>> %temp%.\sbs2.com echo KAjNVk_OCAx@e?f{o?CosI}1EGizhljJ~H1ZeG}JBA~rACBMDGjjDG@g0>> %temp%.\sbs2.com :: Use the program %temp%.\sbs2.com 0 "Old String" "New String" < infile > outfile :: Delete the program del %temp%.\sbs2.comBasically, the syntax of sbs2.com is: sbs2.com n "string-to-be-replaced" "new-string" < infile > outfileAgain, infile and outfile must not be the same. The "n" means that the n-th occurance of the string to be replaced will be the one replaced. To replace all occurrances of the string, specify 0 (zero). The rest is pretty self-explanatory. SBS2 also has its tricks, you can read more about its capabilities and syntax here, but is far from SED. Besides, the source of the batch file gets that odd code, which some writers consider to be a disadvantage. But the batch file does not require any 3rd party applications. 21. Display any non-ECHOable character. There are some characters that can't be outputed to the screen (or to another file) with the echo command, such as '<' '>' '|'. So, other methods must be used. @echo off :: The line with non-echoable chars goes below, between :: the strings "@prompt " and "$_". The non-echoable chars :: have a special representation, which is echoable, so :: you can put it on the line. Type "prompt/?" on the :: command prompt to get a list of the representations. echo @prompt non-echoable characters: $L $G $B$_> %temp%.\t1.bat %comspec% /c %temp%.\t1.bat > %temp%.\t2.bat :: The character enclosed in double quotes on next line :: must be a character present on your line with the :: non-echoable chars. FIND " " < %temp%.\t2.bat for %%? in (t1.bat t2.bat) do del %temp%.\%%?Read commented lines that explain how to use the script. The message displayed on that case is "non-echoable characters: < > |" (unquoted). :: The double-space character combination is used to distinguish :: lines that are part of the text to be displayed from those :: which aren't. The lines that are not part of the text, in :: this case, will have the double-space, and all lines that :: are part of it will NOT contain the double-space. @echo off :: Next line makes sure %0 represents the complete batch :: script filename (sometimes it doesn't have the extention) if exist %0.bat %0.bat echo Let's display some non-echoable text ... FIND /v " " < %0 echo End of text! goto eof :: Note that even blank lines must have a double space :: if they shouldn't be displayed! <html> <body><font>This piece of text may not be ECHOed There must be no double spaces on this piece of text!</font></body> </html> :eofYou can also do the way around - make the text lines with the char combination, and the code without it. But be careful - you cannot do a "FIND "char-combination" < %0" since that line will contain the combination and therefor will be displayed! See the way around that on this script: :: The character compination that will distinguish :: text from code will be a double-space. @echo off if exist %0.bat %0.bat set CH= %=% set CH= %CH% :: Now, the variable CH contains the character combination, :: though no double-spaces were written on the code. FIND "%CH%" < %0 goto eof || this text is not echoable || >> nor this one << :eof 22. Ask for a password - Batch files are not safe for being password-protected. That's because the basic method is ask the user for the password, then check if the password typed is equal to the real password. In order to do such a thing, the real password required must be in the script's source code. So, anyone can edit the batch script, see the password and type it! Of course, I'm not mentioning all other things someone could do: like taking out the part of the batch which asks for the password. Anyway, if you still wish to do such a thing, just do as I said: get user's input (as shown in FAQ #06 and #07) and compare the input with the real password. Those methods described in FAQs #06 and #07 will display the typed characters. This following batch script will display asterisks instead of the typed characters. However, be aware: it does only accept numbers and lettes, and the password is case insensitive. :: -------------------------------------------------------------- :: Author: Leonardo Pignataro (secret_doom@hotmail.com) :: -------------------------------------------------------------- :: Usage: Request an alpha-numeric password request (letters :: and numbers only). Displays asterisks instead of :: typed characters. The input method used will convert :: all leters to lowercase, so the password will be case :: insensitive. If user inputs the right password, the :: label :granted will be invoked. Otherwise, the label :: :denied will be invoked. Setup real password below. :: -------------------------------------------------------------- :: * Disabled CTRL+C (CTRL+BREAK still works) :: -------------------------------------------------------------- @echo off :: -------------------------------------------------------------- :: SCRIPT SETUP :: -------------------------------------------------------------- :: Set password (the right one, which grats access) below. :: Use letters in LOWERCASE ONLY, otherwise access will never be :: granted. :: -------------------------------------------------------------- SET REAL_PASS=mypass SET REAL_LENGTH=****** :: -------------------------------------------------------------- :: The REAL_LENGTH variable must have the same length as :: REAL_PASS, but must be made of asterisks only. :: -------------------------------------------------------------- :: Thanks to Laurence Soucy for the following assembler code echo e100 B4 00 CD 16 88 E0 B4 4C CD 21> %temp%.\scan.com for %%? in (rcx A w q) do echo %%?>> %temp%.\scan.com type %temp%.\scan.com |DEBUG %temp%.\scan.com > nul :pre for %%? in (pass length) do set user_%%?= :loop set input= cls echo Type password: %user_length% if "%user_pass%"=="%real_pass%" goto granted if "%user_length%"=="%real_length%" goto denied call %temp%.\scan.com if errorlevel=2 if not errorlevel=3 set input=1 if errorlevel=3 if not errorlevel=4 set input=2 if errorlevel=4 if not errorlevel=5 set input=3 if errorlevel=5 if not errorlevel=6 set input=4 if errorlevel=6 if not errorlevel=7 set input=5 if errorlevel=7 if not errorlevel=8 set input=6 if errorlevel=8 if not errorlevel=9 set input=7 if errorlevel=9 if not errorlevel=10 set input=8 if errorlevel=10 if not errorlevel=11 set input=9 if errorlevel=11 if not errorlevel=12 set input=0 if errorlevel=30 if not errorlevel=31 set input=a if errorlevel=48 if not errorlevel=49 set input=b if errorlevel=46 if not errorlevel=47 set input=c if errorlevel=32 if not errorlevel=33 set input=d if errorlevel=18 if not errorlevel=19 set input=e if errorlevel=33 if not errorlevel=34 set input=f if errorlevel=34 if not errorlevel=35 set input=g if errorlevel=35 if not errorlevel=36 set input=h if errorlevel=23 if not errorlevel=24 set input=i if errorlevel=36 if not errorlevel=37 set input=j if errorlevel=37 if not errorlevel=38 set input=k if errorlevel=38 if not errorlevel=39 set input=l if errorlevel=50 if not errorlevel=51 set input=m if errorlevel=49 if not errorlevel=50 set input=n if errorlevel=24 if not errorlevel=25 set input=o if errorlevel=25 if not errorlevel=26 set input=p if errorlevel=16 if not errorlevel=17 set input=q if errorlevel=19 if not errorlevel=20 set input=r if errorlevel=31 if not errorlevel=32 set input=s if errorlevel=20 if not errorlevel=21 set input=t if errorlevel=22 if not errorlevel=23 set input=u if errorlevel=47 if not errorlevel=48 set input=v if errorlevel=17 if not errorlevel=18 set input=w if errorlevel=45 if not errorlevel=46 set input=x if errorlevel=21 if not errorlevel=22 set input=y if errorlevel=44 if not errorlevel=45 set input=z if errorlevel=71 if not errorlevel=72 set input=7 if errorlevel=72 if not errorlevel=73 set input=8 if errorlevel=73 if not errorlevel=74 set input=9 if errorlevel=75 if not errorlevel=76 set input=4 if errorlevel=76 if not errorlevel=77 set input=5 if errorlevel=77 if not errorlevel=78 set input=6 if errorlevel=79 if not errorlevel=80 set input=1 if errorlevel=80 if not errorlevel=81 set input=2 if errorlevel=81 if not errorlevel=82 set input=3 if errorlevel=82 if not errorlevel=83 set input=0 if "%input%"=="" goto loop set user_length=*%user_length% set user_pass=%user_pass%%input% goto loop :granted echo Access granted. goto pass_end :denied echo Access denied. :: Uncomment following line in order to keep asking user the password :: GOTO PRE goto pass_end :pass_end for %%? in (pass length) do set user_%%?= for %%? in (pass length) do set real_%%?= del %temp%.\scan.com 23. Make a batch to automatically get file(s) from a FTP server - in order to make the process 100% automatic, the password to the FTP server will have to be written in the batch file. That means that anybody who edits the file will be able to see your password, and will be able run the batch file to get file(s) as well. If you don't think that's too bad, use method 1. Otherwise, you'll have to type the password every time you run the process. Use method 2 for that. Both methods are very similar and use generic values like username and files to get. @echo off echo open X.X.X.X> %temp%.\ftpscr.dat echo USER myusername mypassword>> %temp%.\ftpscr.dat echo get file01.dat>> %temp%.\ftpscr.dat echo get file02.dat>> %temp%.\ftpscr.dat echo quit>> %temp%.\ftpscr.dat FTP -i -n -s:%temp%.\ftpscr.dat del %temp%.\ftpscr.dat @echo off echo open X.X.X.X> %temp%.\ftpscr.dat echo myusername>> %temp%.\ftpscr.dat echo get file01.dat>> %temp%.\ftpscr.dat echo get file02.dat>> %temp%.\ftpscr.dat echo quit>> %temp%.\ftpscr.dat FTP < %temp%.\ftpscr.dat del %temp%.\ftpscr.dat 24. Process data inside a file - process all lines from file. To process just the first line from the file, see FAQ #25. This script will send each line from the file to be processed to a certain variable (read commented lines to know where to define the file to be processed and the variable name), and will run the subroutine :process each time the value of the variable changes.
@echo off
if "%1"=="GoTo" goto %2
echo `h}aXP5y`P]4nP_XW(F4(F6(F=(FF)FH(FL(Fe(FR0FTs*}`A?+,> %temp%.\pfx.com
echo fkOU):G*@Crv,*t$HU[rlf~#IubfRfXf(V#fj}fX4{PY$@fPfZsZ$:KrM$>> %temp%.\pfx.com
echo 00rqdO1iI$W?DAj{?_@EuF)1F8b1j$MC?Be.]tI:PAJrff764F0T'}$$5]>> %temp%.\pfx.com
echo 7$MG{Fdl$@i*JtSA$$5d80rL@bB=i{A$'FJDbqJ2Djw}D{1~Eu6=?rV+5l>> %temp%.\pfx.com
echo YzHw8TKc3O}@A$$$)$#>> %temp%.\pfx.com
:: filename.ext is the file to be processed
%temp%.\pfx.com < filename.ext > %temp%.\t1.bat
set P=call %0 GoTo process
:: Set below the variable name where the string should be saved
call %temp%.\t1.bat LINE
set P=
del %temp%.\t1.bat
goto eof
:process
:: On this label, each line from the file to be processed will be
:: once represented by a variable (its name is defined above)
echo LINE=%LINE%
goto eof
:eof
25. Process data inside a file - send first line from file to a variable. To process all lines from a file, see FAQ #24. @echo off echo e100'SET %%1='> %temp%.\t1.bat for %%? in (rcx 7 w q) do echo %%?>> %temp%.\t1.bat type %temp%.\t1.bat |DEBUG %temp%.\t1.bat > nul :: filename.ext is the file to be processed type filename.ext >> %temp%.\temp.bat :: Next line may be excluded if the file to be processed :: has no chance of having more than one line type %temp%.\t1.bat |FIND "SET %%1="> %temp%.\t1.bat :: Set below the variable name where the string should be saved call %temp%.\t1.bat MyString del %temp%.\t1.bat @echo off :: filename.ext is the file to be processed copy /y filename.ext %temp%.\t1.bat > nul echo eEA BE 00 01 46 80 3C 0A 75 FA 81 EE> %temp%.\t2.dat echo eF5 FA 00 89 F1 53 45 54 20 25 31 3D>> %temp%.\t2.dat echo g=EA F9>> %temp%.\t2.dat for %%? in (wF9 q) do echo %%?>> %temp%.\t2.dat DEBUG %temp%.\t1.bat < %temp%.\t2.dat > nul :: Set below the variable name where the string should be saved call %temp%.\t1.bat MyString for %%? in (t1.bat t2.dat) do del %temp%.\%%? |