You are on page 1of 42

Getting Started with Windows Batch Scripting

Windows batch scripting is incredibly accessible – it works on just about any modern Windows machine. You can

create and modify batch scripts on just about any modern Windows machine. The tools come out of the box: the

Windows command prompt and a text editor like Notepad.exe. It’s definitely far from the best shell scripting langauge,

but, it gets the job done. It’s my “duct tape” for Windows.

Launching the Command Prompt

Windows gurus launch the command prompt using the keyboard shortcut Windows Logo Key+R (i.e., “Run”) >

Type cmd.exe then Enter. This is way faster than navigating the Windows Start Menu to find the Command


Editing Batch Files

The universal text editor for batch files is Notepad (Windows Logo Key + R > Type notepad then Enter). Since

batch files are just ASCII text, you can probably use just about any text editor or word processor. Very few editors do

anything special for Batch files like syntax highlighting or keyword support, so notepad is good enough fine and will

likely be installed on just about every Windows system you encounter.

Viewing Batch Files

I would stick with Notepad for viewing the contents of a batch file. In Windows Explorer (aka, “My Computer”), you

should be able to view a batch file in Notepad by right clicking the file and seleting Edit from the context menu. If you

need to view the contents within a command prompt window itself, you can use a DOS command like TYPE

myscript.cmd or MORE myscript.cmd or EDIT myscript.cmd

Batch File Names and File Extensions

Assuming you are using Windows XP or newer, I recommend saving your batch files with the file extension .cmd.

Some seriously outdated Windows versions used .bat, though I recommend sticking with the more

modern .cmdto avoid some rare side effects with .bat files.

With the .cmd file extension, you can use just about filename you like. I recommend avoiding spaces in filenames, as

spaces only create headaches in shell scripting. Pascal casing your filenames is an easy way to avoid spaces

(e.g., HelloWorld.cmd instead of Hello World.cmd). You can also use punctuation characters like . or -

or _ (e.g. Hello.World.cmd, Hello-World.cmd, Hello_World.cmd).

Another thing with names to consider is avoiding names that use the same name of any built-in commands, system

binaries, or popular programs. For example, I would avoid naming a script ping.cmd since there is a widely used

system binary named ping.exe. Things might get very confusing if you try to run ping and inadvertently

call ping.cmd when you really wanted ping.cmd. (Stay tuned for how this could happen.) I might called the

script RemoteHeartbeat.cmd or something similar to add some context to the script’s name and also avoid any
naming collisions with any other executable files. Of course, there could be a very unique circumstance in which you

want to modify the default behavior of ping in which this naming suggestion would not apply.

Saving Batch Files in Windows

Notepad by default tries to save all files as plain jane text files. To get Notepad to save a file with a .cmd extension, you

will need to change the “Save as type” to “All Files (.)”. See the screenshot below for an example of saving a script

named “HelloWorld.cmd” in Notepad.

SIDEBAR: I’ve used a shortcut in this screenshot that you will learn more about later. I’ve saved the file to my “user profile
folder” by naming the file %USERPROFILE%\HelloWorld.cmd . The %USERPROFILE% keyword is the Windows
environmental variable for the full path to your user profile folder. On newer Windows systems, your user profile folder will
typically be C:\Users\<your username> . This shortcut saves a little bit of time because a new command prompt will
generally default the “working directory” to your user profile folder. This lets you run HelloWorld.cmd in a new command
prompt without changing directories beforehand or needing to specify the path to the script.

Running your Batch File

The easy way to run your batch file in Windows is to just double click the batch file in Windows Explorer (aka “My

Computer”). Unfortunately, the command prompt will not give you much of a chance to see the output and any errors.

The command prompt window for the script will disappear as soon as the script exits. (We will learn how to handle

this problem in Part 10 – Advanced Tricks ).

When editing a new script, you will likely need to run the batch file in an existing command window. For newbies, I

think the easiest foolproof way to run your script is to drag and drop the script into a command prompt window. The

command prompt will enter the full path to your script on the command line, and will quote any paths containing


Some other tips to running batch files:

 You can recall previous commands using the up arrow and down arrow keys to navigate the command line history.

 I usually run the script as %COMPSPEC% /C /D "C:\Users\User\SomeScriptPath.cmd" Arg1 Arg2 Arg3 This

runs your script in a new command prompt child process. The /C option instructs the child process to quit when your script quits.

The /D disables any auto-run scripts (this is optional, but, I use auto-run scripts). The reason I do this is to keep the command

prompt window from automatically closing should my script, or a called script, call the EXITcommand. The EXIT command

automatically closes the command prompt window unless the EXIT is called from a child command prompt process. This is

annoying because you lose any messages printed by your script.


The official way to add a comment to a batch file is with the REM (Remark) keyword:

REM This is a comment!

The power user method is to use ::, which is a hack to uses the the label operator : twice, which is almost always


Most power authors find the :: to be less distracting than REM. Be warned though there are a few places where ::will

cause errors.

:: This is a comment too!! (usually!)

For example, a FOR loop will error out with :: style comments. Simply fall back to using REM if you think you have a

situation like this.

Silencing Display of Commands in Batch Files

The first non-comment line of a batch file is usually a command to turn off printing (ECHO’ing) of each batch file line.


The @ is a special operator to suppress printing of the command line. Once we set ECHO’ing to off, we won’t need

the @ operator again in our script commands.

You restore printing of commands in your script with:

Upon exit of your script, the command prompt will automatically restore ECHO to it’s previous state.

Debugging Your Scripts

Batch files invole a lot of trial and error coding. Sadly, I don’t know of any true debugger for Windows batch scripts.

Worse yet, I don’t know of a way to put the command processor into a verbose state to help troubleshoot the script

(this is the common technique for Unix/Linux scripts.) Printing custom ad-hoc debugging messages is about your only

option using the ECHO command. Advanced script writers can do some trickery to selectively print debugging

messages, though, I prefer to remove the debugging/instrumentation code once my script is functioning as desired.
Variable Declaration

DOS does not require declaration of variables. The value of undeclared/uninitialized variables is an empty string,

or "". Most people like this, as it reduces the amount of code to write. Personally, I’d like the option to require a

variable is declared before it’s used, as this catches silly bugs like typos in variable names.

Variable Assignment

The SET command assigns a value to a variable.

SET foo=bar

NOTE: Do not use whitespace between the name and value; SET foo = bar will not work but SET foo=bar will


The /A switch supports arthimetic operations during assigments. This is a useful tool if you need to validated that user

input is a numerical value.

SET /A four=2+2

A common convention is to use lowercase names for your script’s variables. System-wide variables, known as

environmental variables, use uppercase names. These environmental describe where to find certain things in your

system, such as %TEMP% which is path for temporary files. DOS is case insensitive, so this convention isn’t enforced

but it’s a good idea to make your script’s easier to read and troubleshoot.

WARNING: SET will always overwrite (clobber) any existing variables. It’s a good idea to verify you aren’t overwriting

a system-wide variable when writing a script. A quick ECHO %foo% will confirm that the variable foo isn’t an existing

variable. For example, it might be tempting to name a variable “temp”, but, that would change the meaning of the

widely used “%TEMP%” environmental varible. DOS includes some “dynamic” environmental variables that behave

more like commands. These dynamic varibles include %DATE%, %RANDOM%, and %CD%. It would be a bad idea to

overwrite these dynamic variables.

Reading the Value of a Variable

In most situations you can read the value of a variable by prefixing and postfixing the variable name with

the %operator. The example below prints the current value of the variable foo to the console output.

C:\> SET foo=bar

C:\> ECHO %foo%

There are some special situations in which variables do not use this % syntax. We’ll discuss these special cases later in

this series.

Listing Existing Variables

The SET command with no arguments will list all variables for the current command prompt session. Most of these

varaiables will be system-wide environmental variables, like %PATH% or %TEMP%.

NOTE: Calling SET will list all regular (static) variables for the current session. This listing excludes the dynamic

environmental variables like %DATE% or %CD%. You can list these dynamic variables by viewing the end of the help text

for SET, invoked by calling SET /?

Variable Scope (Global vs Local)

By default, variables are global to your entire command prompt session. Call the SETLOCAL command to make

variables local to the scope of your script. After calling SETLOCAL, any variable assignments revert upon

calling ENDLOCAL, calling EXIT, or when execution reaches the end of file (EOF) in your script.

This example demonstrates changing an existing variable named foo within a script named HelloWorld.cmd. The

shell restores the original value

of %foo% when HelloWorld.cmd exits.

A real life example might be a script that modifies the system-wide %PATH% environmental variable, which is the list of

directories to search for a command when executing a


Special Variables
There are a few special situations where variables work a bit differently. The arguments passed on the command line

to your script are also variables, but, don’t use the %var% syntax. Rather, you read each argument using a single %with

a digit 0-9, representing the ordinal position of the argument. You’ll see this same style used later with a hack to create

functions/subroutines in batch scripts.

There is also a variable syntax using !, like !var!. This is a special type of situation called delayed expansion. You’ll

learn more about delayed expansion in when we discuss conditionals (if/then) and looping.

Command Line Arguments to Your Script

You can read the command line arguments passed to your script using a special syntax. The syntax is a

single %character followed by the ordinal position of the argument from 0 – 9. The zero ordinal argument is the name

of the batch file itself. So the variable %0 in our script HelloWorld.cmd will be “HelloWorld.cmd”.

The command line argument variables are * %0: the name of the script/program as called on the command line;

always a non-empty value * %1: the first command line argument; empty if no arguments were provided * %2: the

second command line argument; empty if a second argument wasn’t provided * …: * %9: the ninth command line


NOTE: DOS does support more than 9 command line arguments, however, you cannot directly read the 10th

argument of higher. This is because the special variable syntax doesn’t recognize %10 or higher. In fact, the shell

reads %10 as postfix the %0 command line argument with the string “0”. Use the SHIFT command to pop the first

argument from the list of arguments, which “shifts” all arguments one place to the left. For example, the the second

argument shifts from position %2 to %1, which then exposes the 10th argument as %9. You will learn how to process a

large number of arguments in a loop later in this series.

Tricks with Command Line Arguments

Command Line Arguments also support some really useful optional syntax to run quasi-macros on command line

arguments that are file paths. These macros are called variable substitution support and can resolve the path,

timestamp, or size of file that is a command line argument. The documentation for this super useful feature is a bit

hard to find – run ‘FOR /?’ and page to the end of the output.

 %~1 removes quotes from the first command line argument, which is super useful when working with arguments to file paths.

You will need to quote any file paths, but, quoting a file path twice will cause a file not found error.

SET myvar=%~1

 %~f1 is the full path to the folder of the first command line argument

 %~fs1 is the same as above but the extra s option yields the DOS 8.3 short name path to the first command line argument

(e.g., C:\PROGRA~1 is usually the 8.3 short name variant of C:\Program Files). This can be helpful when using third

party scripts or programs that don’t handle spaces in file paths.

 %~dp1 is the full path to the parent folder of the first command line argument. I use this trick in nearly every batch file I write to

determine where the script file itself lives. The syntax SET parent=%~dp0 will put the path of the folder for the script file in

the variable %parent%.

 %~nx1 is just the file name and file extension of the first command line argument. I also use this trick frequently to determine the

name of the script at runtime. If I need to print messages to the user, I like to prefix the message with the script’s name, like ECHO

%~n0: some message instead of ECHO some message . The prefixing helps the end user by knowing the output is from

the script and not another program being called by the script. It may sound silly until you spend hours trying to track down an

obtuse error message generated by a script. This is a nice piece of polish I picked up from the Unix/Linux world.

Some Final Polish

I always include these commands at the top of my batch scripts:

SET me=%~n0
SET parent=%~dp0

The SETLOCAL command ensures that I don’t clobber any existing variables after my script exits.

The ENABLEEXTENSIONS argument turns on a very helpful feature called command processor extensions. Trust me,

you want command processor extensions. I also store the name of the script (without the file extension) in a variable

named %me%; I use this variable as the prefix to any printed messages (e.g. ECHO %me%: some message). I also

store the parent path to the script in a variable named %parent%. I use this variable to make fully qualified filepaths

to any other files in the same directory as our script.

Return Code Conventions

By convention, command line execution should return zero when execution succeeds and non-zero when execution

fails. Warning messages typically don’t effect the return code. What matters is did the script work or not?

Checking Return Codes In Your Script Commands

The environmental variable %ERRORLEVEL% contains the return code of the last executed program or script. A very

helpful feature is the built-in DOS commands like ECHO, IF, and SET will preserve the existing value


The conventional technique to check for a non-zero return code using the NEQ (Not-Equal-To) operator of

the IFcommand:

REM do something here to address the error

Another common technique is:


REM do something here to address the error

The ERRORLEVEL 1 statement is true when the return code is any number equal to or greater than 1. However, I don’t

use this technique because programs can return negative numbers as well as positive numbers. Most programs rarely

document every possible return code, so I’d rather explicity check for non-zero with the NEQ 0 style than assuming

return codes will be 1 or greater on error.

You may also want to check for specific error codes. For example, you can test that an executable program or script is

in your PATH by simply calling the program and checking for return code 9009.

ECHO error - SomeFile.exe not found in your PATH

It’s hard to know this stuff upfront – I generally just use trial and error to figure out the best way to check the return

code of the program or script I’m calling. Remember, this is duct tape programming. It isn’t always pretty, but, it gets

the job done.

Conditional Execution Using the Return Code

There’s a super cool shorthand you can use to execute a second command based on the success or failure of a

command. The first program/script must conform to the convention of returning 0 on success and non-0 on failure for

this to work.

To execute a follow-on command after sucess, we use the && operator:

SomeCommand.exe && ECHO SomeCommand.exe succeeded!

To execute a follow-on command after failure, we use the || operator:

SomeCommand.exe || ECHO SomeCommand.exe failed with return code %ERRORLEVEL%

I use this technique heavily to halt a script when any error is encountered. By default, the command processor will

continue executing when an error is raised. You have to code for halting on error.

A very simple way to halt on error is to use the EXIT command with the /B switch (to exit the current batch script

context, and not the command prompt process). We also pass a specific non-zero return code from the failed

command to inform the caller of our script about the failure.

SomeCommand.exe || EXIT /B 1

A simliar technique uses the implicit GOTO label called :EOF (End-Of-File). Jumping to EOF in this way will exit your

current script with the return code of 1.

SomeCommand.exe || GOTO :EOF

Tips and Tricks for Return Codes

I recommend sticking to zero for success and return codes that are positive values for DOS batch files. The positive

values are a good idea because other callers may use the IF ERRORLEVEL 1 syntax to check your script.

I also recommend documenting your possible return codes with easy to read SET statements at the top of your script

file, like this:


Note that I break my own convention here and use uppercase variable names – I do this to denote that the variable is

constant and should not be modified elsewhere. Too bad DOS doesn’t support constant values like Unix/Linux shells.

Some Final Polish

One small piece of polish I like is using return codes that are a power of 2.

This gives me the flexibility to bitwise OR multiple error numbers together if I want to record numerous problems in

one error code. This is rare for scripts intended for interactive use, but, it can be super helpful when writing scripts you

support but you don’t have access to the target systems.


SET /A errno=0



EXIT /B %errno%

If both SomeCommand.exe and OtherCommand.exe fail, the return code will be the bitwise combination of 0x1 and

0x2, or decimal 3. This return code tells me that both errors were raised. Even better, I can repeatedly call the bitwise

OR with the same error code and still interpret which errors were raised.
File Numbers

Each of these three standard files, otherwise known as the standard streams, are referernced using the numbers 0, 1,

and 2. Stdin is file 0, stdout is file 1, and stderr is file 2.


A very common task in batch files is sending the output of a program to a log file. The > operator sends, or redirects,

stdout or stderr to another file. For example, you can write a listing of the current directory to a text file:

DIR > temp.txt

The > operator will overwrite the contents of temp.txt with stdout from the DIR command. The >> operator is a slight

variant that appends the output to a target file, rather than overwriting the target file.

A common technique is to use > to create/overwrite a log file, then use >> subsequently to append to the log file.

SomeCommand.exe > temp.txt

OtherCommand.exe >> temp.txt

By default, the > and >> operators redirect stdout. You can redirect stderr by using the file number 2 in front of the


DIR SomeFile.txt 2>> error.txt

You can even combine the stdout and stderr streams using the file number and the & prefix:

DIR SomeFile.txt 2>&1

This is useful if you want to write both stdout and stderr to a single log file.

DIR SomeFile.txt > output.txt 2>&1

To use the contents of a file as the input to a program, instead of typing the input from the keyboard, use

the <operator.

SORT < SomeFile.txt

Suppressing Program Output

The pseudofile NUL is used to discard any output from a program. Here is an example of emulating the Unix

command sleep by calling ping against the loopback address. We redirect stdout to the NUL device to avoid printing

the output on the command prompt screen.


Redirecting Program Output As Input to Another Program

Let’s say you want to chain together the output of one program as input to another. This is known as “piping” output to

another program, and not suprisingly we use the pipe character | to get the job done. We’ll sort the output of the DIR



A Cool Party Trick

You can quickly create a new text file, say maybe a batch script, from just the command line by redirecting the

command prompt’s own stdin, called CON, to a text file. When you are done typing, hit CTRL+Z, which sends the end-

of-file (EOF) character.

TYPE CON > output.txt

There are a number of other special files on DOS that you can redirect, however, most are a bit dated like like LPT1 for

parallel portt printers or COM1 for serial devices like modems.

Checking that a File or Folder Exists

IF EXIST "temp.txt" ECHO found

Or the converse:

IF NOT EXIST "temp.txt" ECHO not found

Both the true condition and the false condition:

IF EXIST "temp.txt" (
ECHO found
) ELSE (
ECHO not found

NOTE: It’s a good idea to always quote both operands (sides) of any IF check. This avoids nasty bugs when a variable

doesn’t exist, which causes the the operand to effectively disappear and cause a syntax error.

Checking If A Variable Is Not Set

IF "%var%"=="" (SET var=default value)


IF NOT DEFINED var (SET var=default value)

Checking If a Variable Matches a Text String

SET var=Hello, World!

IF "%var%"=="Hello, World!" (
ECHO found

Or with a case insensitive comparison

IF /I "%var%"=="hello, world!" (
ECHO found
Artimetic Comparisons

SET /A var=1

IF /I "%var%" EQU "1" ECHO equality with 1

IF /I "%var%" NEQ "0" ECHO inequality with 0

IF /I "%var%" GEQ "1" ECHO greater than or equal to 1

IF /I "%var%" LEQ "1" ECHO less than or equal to 1

Checking a Return Code


ECHO execution failed
Old School with GOTO

The old-school way of looping on early versions of DOS was to use labels and GOTO statements. This isn’t used much

anymore, though it’s useful for looping through command line arguments.

SET arg=%~1
ECHO %arg%
GOTO :args

New School with FOR

The modern way to loop through files or text uses the FOR command. In my opinion, FOR is the single most powerful

command in DOS, and one of the least used.

GOTCHA: The FOR command uses a special variable syntax of % followed by a single letter, like %I. This syntax is

slightly different when FOR is used in a batch file, as it needs an extra percent symbol, or %%I. This is a very common

source of errors when writing scripts. Should your for loop exit with invalid syntax, be sure to check that you have

the %% style variables.

Looping Through Files


Looping Through Directories


Recursively loop through files in all subfolders of the %TEMP% folder

FOR /R "%TEMP%" %I IN (*) DO @ECHO %I

Recursively loop through all subfolders in the %TEMP% folder

FOR /R "%TEMP%" /D %I IN (*) DO @ECHO %I

Variable Substitution for Filenames

Functions are de facto way to reuse code in just about any procedural coding language. While DOS lacks a bona fide

function keyword, you can fake it till you make it thanks to labels and the CALL keyword.

There are a few gotchas to pay attention to:

1. Your quasi functions need to be defined as labels at the bottom of your script.

2. The main logic of your script must have a EXIT /B [errorcode] statement. This keeps your main logic from falling

through into your functions.

Defining a function

In this example, we’ll implement a poor man’s version of the *nix tee utility to write a message to both a file and the

stdout stream. We’ll use a variable global to the entire script, %log% in the function.


:: script global variables

SET me=%~n0
SET log=%TEMP%\%me%.txt

:: The "main" logic of the script

IF EXIST "%log%" DELETE /Q %log% >NUL

:: do something cool, then log it

CALL :tee "%me%: Hello, world!"

:: force execution to quit at the end of the "main" logic


:: a function to write to a log file and write to stdout

ECHO %* >> "%log%"

Calling a function

We use the CALL keyword to invoke the quasi function labelled :tee. We can pass command line arguments just like

we’re calling another batch file.

We have to remember to EXIT /B keyword at the end our function. Sadly, there is no way to return anything other

than an exit code.

Return values

The return value of CALL will always be the exit code of the function. Like any other invokation of an executable, the

caller reads %ERRORLEVEL% to get the exit code.

You have to get creative to pass anything other than integer return codes. The function can ECHO to stdout, letting the

caller decide to handle the output by pipeling the output as the input to another executable, redirecting to a file, or

parsing via the FOR command.

The caller could also pass data by modifying a global variable, however, I try to avoid this approach.
Robust parsing of command line input separates a good script from a great script. I’ll share some tips on how I parse


The Easy Way to read Command Line Arguments

By far the easiest way to parse command line arguments is to read required arguments by ordinal position.

Here we get the full path to a local file passed as the first argument. We write an error message to stderr if the file does

not exist and exit our script:

SET filepath=%~f1

IF NOT EXIST "%filepath%" (

ECHO %~n0: file not found - %filepath% >&2

Optional parameters

I assign a default value for parameters as such

SET filepath=%dp0\default.txt

:: the first parameter is an optional filepath

IF EXIST "%~f1" SET filepath=%~f1


Named Parameters

Variable Number of Arguments

Reading user input

SET /P "Continue [y/n]>" %confirm%
FINDSTR /I "^(y|n|yes|no)$" > NUL || GOTO: confirm
I use basic logging facilities in my scripts to support troubleshooting both during execution and after execution. I use

basic logging as a way to instrument what my scripts are doing at runtime and why. I remember watching a network

operations center trying to troubleshoot a legacy batch process where the sysadmins literrally had to try to read the

lines of a console window as they trickled by. This technique worked fine for years when the batch machines used dial-

up modems for connectivity to remote resources. However, the advent of brooadband meant the batch script executed

faster than anyone could read the output. A simple log file would have made troubleshooting work much easier for

these sysadmins.

Log function

I really like the basic tee implementation I wrote in Part 7 – Functions.


:: script global variables

SET me=%~n0
SET log=%TEMP%\%me%.txt

:: The "main" logic of the script

IF EXIST "%log%" DELETE /Q %log% >NUL

:: do something cool, then log it

CALL :tee "%me%: Hello, world!"

:: force execution to quit at the end of the "main" logic


:: a function to write to a log file and write to stdout

ECHO %* >> "%log%"

This tee quasi function enable me to write output to the console as well as a log file. Here I am reusing the same log

file path, which is saved in the users %TEMP% folder as the name of the batch file with a .txt file extension.

If you need to retain logs for each execution, you could simply parse the %DATE% and %TIME% variables (with the

help of command line extensions) to generate a unique filename (or at least unique within 1-second resolution).

REM create a log file named [script].YYYYMMDDHHMMSS.txt


Taking a queue from the *nix world, I also like to include a prefix custom output from my own script as script:

some message. This technique drastically helps to sort who is complaining in the case of an error.
Displaying startup parameters

I also like to display the various runtime conditions for non-interactive scripts, like something that will be run on a

build server and redirected to a the build log.

Sadly, I don’t know of any DOS tricks (yet) to discrimintate non-interactive sessions from interactive sessions. C# and

.Net has the System.Environment.UserInteractive property to detect if this situation; *nix has some tricks

with tty file descriptors. You could probably hack up a solution by inspecting a custom environmental variable

like %MYSCRIPT_DEBUG% that defaults to being false.

Boilplate info

I like to include a header on all of scripts that documents the who/what/when/why/how. You can use the ::comment

trick to make this header info more readable:

:: Name: MyScript.cmd
:: Purpose: Configures the FooBar engine to run from a source control tree path
:: Author:
:: Revision: March 2013 - initial version
:: April 2013 - added support for FooBar v2 switches


:: variables
SET me=%~n0

@EXIT /B 0

Conditional commands based on success/failure

The conditional operators || and && provide a convenient shorthand method to run a 2nd command based on the

succes or failure of a 1st command.

The && syntax is the AND opeartor to invoke a 2nd command when the first command returns a zero (success) exit


DIR myfile.txt >NUL 2>&1 && TYPE myfile.txt

The || syntax is an OR operator to invoke a 2nd command when the first command returns a non-zero (failure) exit


DIR myfile.txt >NUL 2>&1 || CALL :WARNING file not found - myfile.txt

We can even combined the techniques. Notice how we use the () grouping construct with && to run 2 commands

together should the 1st fail.

DIR myfile.txt >NUL 2>&1 || (ECHO %me%: WARNING - file not found - myfile.txt >2 &&
EXIT /B 1)

Getting the full path to the parent directory of the script

:: variables
PUSHD "%~dp0" >NUL && SET root=%CD% && POPD >NUL

Making a script sleep for N seconds

You can use PING.EXE to fake a real *nix style sleep command.

:: sleep for 2 seconds


Supporting “double-click” execution (aka invoking from Windows Explorer)

Test if %CMDCMDLINE% is equal to %COMSPEC% If they are equal, we can assume that we are running in an interactive

session. If not equal, we can inject a PAUSE into the end of the script to show the output. You may also want to change

to a valid working directory.

SET interactive=0


IF %ERRORLEVEL% == 0 SET interactive=1

ECHO do work

IF "%interactive%"=="0" PAUSE
La sintaxis normal del comando FOR es:

FOR %var IN (lista) DO (


Pero si lo vamos a usar dentro de un archivo BAT será así:

FOR %%var IN (lista) DO (


Observa que la variable "var" ahora va precedida por dos simbolos de "%". Además, si este for está dentro
de un archivo BAT, el nombre de la variable debe ser UNA SOLA LETRA (p.ej: %%n, %%i, %%j , etc )


Para que la variable del FOR vaya tomando distintos valores dentro de una lista determinada:

for %A in (uno dos tres cuatro) do (

echo %A

Esto sacará por pantalla las palabras que están entre parentesis, separadas linea a linea.

Y si el "for" está dentro de un archivo BAT, y tome los valores posicionales que se pasan cuando se llama
al archivo desde la consola:

for %%x in (%*) do (

echo %%x


Si queremos recorrer una lista de archivos de un determinado directorio (solo archivos, no directorios):

for %f in (*) do (
echo %f

y si queremos mover a la papelera de reciclaje algunos archivos con determinadas extensiones:

for %f in (*.jpg, *.mp3, *.bmp) do (

move %f c:\recycler
FOR /R [ruta] %V IN (lista) DO comando

FOR /D %V IN (lista) DO comando


FOR /L %V IN (inicio, incremento, fin) DO comando


FOR /F ["tokens=... delims=..."] %V IN (archivo | 'comando' | "cadena")
DO comando

EJEMPLO3: (parametro /R)

Recorrer y mostrar todos los archivos de la unidad C: empezando en su directorio raiz, y recorriendo
recursivamente el resto de directorios que contiene:

for /R c:\ %v in (*) do (

echo %v

Una variacion de este ejemplo sería el buscar un determinado tipo de archivos dentro de un directorio
recursivamente. Voy a buscar todos los archivos "dll" y "exe" dentro del directorio "windows":

for /R c:\windows %v in (*.dll, *.exe) do (

echo %v

EJEMPLO4: (parametro /D)

Si lo que me interesa es listar los directorios en vez de los archivos:

for /D %v in (*) do (
echo %v

Y si lo quiero hacer recursivamente puedo añadir "/R" al comando anterior:

for /R /D %v in (*) do (
echo %v

Y si quiero hacerlo recursivamente desde un directorio concreto (p.ej: c:\windows):

for /R c:\windows /D %v in (*) do (

echo %v

EJEMPLO5: (parametro /L)

Para crear un tipico bucle contador de 1 a 10, saltando de 1 en 1:

for /L %x in (1, 1, 10) do (

echo %x

Y saltando de 2 en 2

for /L %x in (1, 2, 10) do (

echo %x

Y de 3 en 3:

for /L %x in (1, 3, 10) do (

echo %x


Hay que observar que el primer numero dentro del parentesis es el valor inicial que tomará la variable
"%x", el segundo numero es el incremento que sufrirá dicha variable en la proxima iteracion del FOR, y el
tercer número es el valor máximo que tomará dicha variable y que cuando alcance o supere dicho valor, el
bucle FOR terminará de ejecutarse.


Archivo BAT que crea varios archivos vacios y los llama a todos con el mismo nombre, pero terminados en
un numero diferente:

@echo off

set/p nombre=Indica el nombre de los archivos a crear:

set/p num=Cuantos archivos hay que crear?:

for /L %%x in (1, 1, %num%) do (

echo 2> %%x%nombre

EJEMPLO7: (parametro /F)

Recorrer un archivo y mostrar solo la primera palabra de cada linea:

for /F %%x in (fichero.txt) do (

echo %%x

El FOR va recorriendo todas las lineas, y cada linea se ha dividido en "tokens" (token=palabra). La
variable del for almacena la primera palabra de cada linea.
EJEMPLO8: (parametro /F con tokens)

Podemos seleccionar varios tokens mediante la clausula tokens=. Los distintos tokens se irán guardando
automáticamente en variables alfabeticamente consecutivas a partir de la variable creada en el for.

En el siguiente ejemplo nos quedamos con los tokens (palabras) 1, 3 y 5 de cada linea:

for /F "tokens=1,3,5" %%a in (fichero.txt) do (

echo %%a %%b %%c

observa que aunque yo solamente he definido la variable "%%a" en el FOR, las variables "b" y "c" se
crean automaticamente.

Podemos escoger rangos, como en el siguiente ejemplo, en el que vamos a escoger los tokens del 1 al 3,
y además el token 5

for /F "tokens=1-3,5" %%a in (fichero.txt) do (

echo %%a %%b %%c %%d

O si queremos coger la linea completa a partir de la septima palabra:

for /F "tokens=7*" %%a in (fichero.txt) do (

echo %%a

Y si queremos coger la linea completa:

for /F "tokens=*" %%a in (fichero.txt) do (

echo %%a

EJEMPLO8: (parametro /F con delimitadores)

Además de la clausula "tokens" con el parámetro "/F", podemos usar la clausula "delims", que indica la
separacion entre los distintos tokens. Por defecto, los caracteres delimitadores entre tokens son los
espacios en blanco y los tabuladores.

En el siguiente ejemplo anulamos los delimitadores y obtenemos lo mismo que antes:

for /F "delims=" %%a in (fichero.txt) do (

echo %%a

Si queremos usar como delimitadores los simbolos de puntuacion, como el punto, la coma, el punto y
coma, etc...:

for /F "delims=.,;:" %%a in (fichero.txt) do (

echo %%a

esto solo imprimirá por pantalla la linea completa hasta que encuentre alguno de los delimitadores que le
hemos indicado.

También se pueden combinar delimitadores y tokens:

for /F "tokens=7* delims=,." %%a in (fichero.txt) do (

echo %%a

También se pueden usar otras cláusulas como:

 skip=n, que se saltaría las n primeras líneas del archivo, empezando a procesar la n+1
 eol=carácter, que interpreta las líneas que comienzan por ese carácter como líneas de
comentarios y se las salta.
 useback, que cambia la semantica de la lista: (`comando`), ('cadena'), (archivo) ("archivo1"
"archivo2" "archivo3")

EJEMPLO9: (uso de comandos dentro de la Lista)

Se puede sustituir la lista de valores que tiene que tomar la variable, por la ejecución de un comando,
como en el siguiente ejemplo:

for /F "tokens=15 delims=:" %%a in (ipconfig ^| find IP) do (

echo %%a

Observar el caracter de escape "^" necesario para que la tubería se interprete dentro del comando en vez
de ser interpretado para el for

EJEMPLO10: (parámetro /F con cadenas)

Podemos extraer distintas partes del valor de una variable. Por ejemplo, si mostramos el valor de la
variable "date", nos dará una fecha con el siguiente formato:


donde el dia, la fecha y la hora están separados por un carácter que sirve de delimitador: "/"

Para extraer las distintas partes de la fecha utilizamos el siguiente ejemplo (dentro de un archivo BAT):

for /F "tokens=1-3 delims=/:" %%a in (%date%) do (

echo Hoy es %%a del mes %%b del año %%c

lo cual nos proporciona la siguiente salida por pantalla:

Hoy es 07 del mes 05 del año 2012

EJEMPLO 11: Variables dentro de un FOR

Hay veces que necesitamos usar y actualizar otras variables en el FOR, a parte de la que se crea en la
cabecera del mismo, o incluso anidar un FOR dentro de otro FOR.

Para poder acceder a dichas variables hay que poner al principio del archivo BAT la siguiente linea (es
MUY MUY IMPORTANTE ponerlo, porque sino no funcionará bien nuestro programa BAT):

setlocal enabledelayedexpansion

y además para acceder al valor de dichas variables hay que usar el simbolo de admiración "!", en lugar del
simbolo "%"

Por ejemplo, el siguiente archivo BAT nos muestra la tabla de multiplicar de un numero que yo le indique
por teclado:


@echo off
setlocal enabledelayedexpansion

rem En la variable num guardo que tabla de multiplicar quiero ver

set /p num=Quieres ver la tabla de multiplicar del numero...

echo TABLA DEL %num%

echo ===============
echo .

for /L %%a in (1,1,10) do (

set /a result=%num%*%%a
echo %num% * %%a = !result!
rem Hay que observar que para acceder al valor de la variable
rem "result" hay que cambiar los caracteres de porcentaje "%"
rem por caracteres de admiracion "!"

Si quiero ver todas las tablas de multiplicar, debo anidar un FOR dentro de otro, y el archivo BAT quedaría


@echo off
setlocal enabledelayedexpansion

for /L %%i in (1,1,10) do (

echo TABLA DEL %%i

echo ===============
echo .

for /L %%a in (1,1,10) do (

set /a result=%%i*%%a
echo %%i * %%a = !result!


Si no queremos usar el comando "setlocal enabledelayedexpansion" podemos ejecutar nuestro archivo

BAT desde un terminal con "sustitución retardada". Para ello ejecutamos lo siguiente:

cmd /v:on


Aquí iré añadiendo, cuando tenga tiempo, las soluciones a lo que me vais planteando los que comentais
en esta entrada.

 El usuario Nano me preguntó:

"Me gustaría crear un archivo bat, el cual me mostrara la fecha de creación de un archivo.
el resultado debería ser:

La fecha del archivo es: 20/05/2013"


@echo off

echo Este programa BAT te pide el nombre de un archivo y te muestra la fecha en

la que fue creado


rem en la variable "archivo" meto el nombre del archivo

set /p archivo="Introduce el nombre del archivo:"

rem ahora con el siguiente "for" extraigo (con el comando "find") la unica
linea donde estan los caracteres "/" del listado que me saca el comando dir,
porque ahi esta la fecha que quiero obtener

rem es muy importante poner las comillas simples en su lugar correspondiente

rem y tambien es muy importante poner el acento circunflejo "^" justo antes del
caracter filtro "|", porque sino no funcionara

for /F "tokens=1" %%x in ('dir %archivo% ^| find "/"') do (

echo El archivo %archivo% fue creado el %%x

 La usuaria Alejandra me preguntó:

"Quiero recorrer a partir de un directorio todos los archivos y renombrar los que tienen una extension, por
ejemplo jpg
tendria que renombrar todos incusive si hay subdirectorios."
Esto NO SE PUEDE HACER, porque a cada archivo le tendrías que poner un nombre diferente (no puede
haber 2 o más archivos que tengan el mismo nombre y la misma extensión), tendrías que ir cambiandoselo
uno por uno.

Sin embargo, si puedo cambiar la extensión de todos los archivos de un determinado directorio, que
tengan una extensión en concreto (por ejemplo ".avi", ".mp3", o cualquier otra), este sería el archivo BAT:


@echo off
setlocal enabledelayedexpansion
echo Este archivo BAT recorre un directorio y sus subdirectorios y renombra los
archivos que tengan una determinada extension:
set /p dir="Introduce la ruta del directorio:"
set /p ext="Introduce la extension de los archivos que se van a cambiar:"
set /p nueva="Introduce la nueva extensión que van a tener esos archivos:"

rem Me situo en el directorio en cuestion

cd %dir%

rem Cambio la extension de los archivos del directorio actual

ren *.!ext! *.!nueva!

rem Con el siguiente "for" me voy metiendo en los subdirectorios del directorio
actual y les voy cambiando la extension a los archivos elegidos

for /D /R %%x in (*) do (

cd %%x
ren *.!ext! *.!nueva!
cd ..

 El usuario Anonimo me preguntó:

"Quiero hacer un archivo BAT que: Programe la ejecución de una copia de seguridad del contenido de una
carpeta a otra a la hora que se pase como parámetro."


Aqui hay que preguntar 3 cosas al usuario: la carpeta que quiere copiar, la carpeta destino, y la hora a la
que se efectuará esa copia. Y después usar el comando "at" para programar la tarea a una hora
determinada, y el comando "xcopy" para copiar un directorio con todo su contenido.

@echo off

set /p origen="Escribe la ruta de la carpeta que quieres copiar:"
set /p destino="Escribe la ruta de destino"
set /p hora="Escribe la hora a la que se hara esta copia: hh:mm"
at %hora% xcopy %origen% %destino%

Pero si la hora se la pasamos desde la linea de comandos sería así:

@echo off

set /p origen="Escribe la ruta de la carpeta que quieres copiar:"
set /p destino="Escribe la ruta de destino"

at %1 xcopy %origen% %destino%

 El usuario Anonimo también me preguntó:

"Quiero hacer un archivo BAT que: Muestre el nombre y tamaño de los archivos de la carpeta
activa cuyo tamaño sea superior a la cantidad que se indique como parámetro"


Con un for hay que ir recorriendo todos los archivos del directorio actual, y comparar su tamaño con el
número de bytes que se le ha pasado como parametro posicional número 1 (%1).

@echo off
set enabledelayedexpansion

echo Los archivos mas grandes de %1 bytes son:


REM Recorro todos los archivos del directorio actual y comparo si

REM su tamaño es mayor que el tamaño del archivo pasado por la
REM linea de comandos

for %%a in (*) do (

for /F "tokens=3" %%t in ('dir /N /-C %%a ^| find "/"') do (
if %%t gtr %1 echo %%a = %%t bytes

REM He usado el caracter "/" para filtrar las lineas generadas

REM por el comando dir, puesto que las lineas que contienen ese
REM caracter, son las que tienen una fecha, y por tanto tienen
REM los datos de un archivo concreto.
REM Además uso las opciones /N y /-C del comando dir para que
REM se muestren todos los datos de los archivos, entre ellos
REM el tamaño, y la opcion /-C evita que aparezca el separador
REM de miles en el tamaño del archivo, que luego al hacer la
REM comparación me dará problemas si aparece.

 El usuario Victor Castillejo me preguntó:

"Quiero hacer un archivo BAT que: me muestre la linea numero 4 de un archivo en concreto"

He hecho un archivo bat mejor que te pida el nombre de un archivo, y una linea inicial y una final y te
imprima todas las lineas que están entre ambos numeros


@echo off
setlocal enabledelayedexpansion

set/p nombre=Indica el nombre del archivo:

set/p numero=Indica el numero de linea inicial que quieres mostrar:
set/p final=Indica el numero de linea final que quieres mostrar:

set num=1

for /f "tokens=*" %%a in (%nombre%) do (

if !num! geq %numero% (
if !num! leq %final% echo %%a
set /a num = !num! + 1

 El usuario Anonimo me preguntó:

"Quiero hacer un archivo BAT que me muestre las 30 ultimas lineas de un archivo en concreto"

He hecho un archivo bat mejor que te pida el nombre de un archivo, y las lineas finales, y te imprima las
lineas finales que el usuario indique


@echo off
setlocal enabledelayedexpansion

set/p nombre=Indica el nombre del archivo:

set/p lineas=Indica cuantas lineas quieres mostrar del final del


set num=1

REM Con el siguiente for contare cuantas lineas tiene el archivo

for /f "tokens=*" %%a in (%nombre%) do (
set /a num = !num! + 1

echo El archivo %nombre% tiene %num% lineas. Las ultimas %lineas% lineas son
las siguientes:

REM En la variable inicio estara el numero de la linea desde la que tenemos que
imprimir por pantalla
set /a inicio = !num! - !lineas!
set num=1

for /f "tokens=*" %%a in (%nombre%) do (

if !num! geq %inicio% (
echo %%a
set /a num = !num! + 1

 El usuario Fran me preguntó:

"Quiero hacer un archivo BAT que lea un fichero que tiene un indice (un numero al principio de cada linea)
y que las lineas que estan entre 0 y 249 las guarde dentro de un archivo (archivo1.txt) y las restantes
lineas las guarde en otro archivo (archivo2.txt)"


@echo off
setlocal enabledelayedexpansion

REM Los archivos resultantes van a ser "archivo1" y "archivo2"

REM Al principio, estos archivos deben estar vacios
echo. > archivo1.txt
echo. > archivo2.txt

set/p nombre=Indica el nombre del archivo:

REM ahora tomare las lineas del archivo de una en una

REM leyendo su primer token, que sera el indice, y el
REM resto de la linea, que sera lo que meta en el fichero
REM resultante
for /f "tokens=1,*" %%a in (%nombre%) do (
if %%a lss 250 (
echo %%a %%b >> archivo1.txt
) else (
echo %%a %%b >> archivo2.txt

 El usuario Angel (anonimo) me preguntó:

"Quiero hacer un archivo BAT que recorra el árbol que cuelga de un directorio, y me copie un archivo
determinado en cada uno de los directorios de dicho árbol"


@echo off

REM Guardo en la variable ruta la ruta del directorio actual

set ruta=%cd%

set /p nombre=Escribe el archivo del directorio actual que quieres copiar:

REM Ahora en la variable ruta, genero la ruta absoluta del

REM archivo que quiero copiar
set ruta=%ruta%\%nombre%

REM Con el siguiente FOR recorro recursivamente todos los

REM directorios del directorio actual y copio el archivo
REM que he seleccionado dentro de ellos
for /R /D %%v in (*) do (
copy "%ruta%" "%%v\%nombre%"

 El usuario Jorge Rainof me preguntó:

"Quiero hacer un archivo BAT que sume los datos numericos que se encuentran en las lineas de un fichero
de texto. Estos numeros tienen separador de miles"


@echo off
setlocal enabledelayedexpansion

set suma=0

set /p archivo=Que archivo contiene los numeros para sumar?:

for /f "tokens=1-5 delims=." %%a in (!archivo!) do (

set num=%%a%%b%%c%%d%%e
set /a suma=!suma! + !num!
echo + !num!

echo ------------------------
echo !suma!


OTRA SOLUCIÓN (propuesta por Jorge Rainoff):

@echo off
setlocal enabledelayedexpansion

set suma=0

set /p archivo=Que archivo contiene los numeros para sumar?:

REM en el siguiente "for", a diferencia del anterior, solo

REM voy a coger el primer "token", asi que no hace falta
REM que lo indique explicitamente en el "for".
REM En la variable "%%a" estará el número que quiero sumar,
REM y para quitarle los puntos intermedios, asigno su valor
REM a otra variable llamada "num", y a ésta le quito los
REM puntos con la siguiente instrucción: "set num=!num:.=!"
for /f %%a in (!archivo!) do (
set num=%%a
set num=!num:.=!
echo + !num!
set /a suma= !suma! + !num!

echo ------------------------
echo !suma!


 El usuario Anonimo me preguntó:

"Quiero hacer un archivo BAT que copie el ultimo archivo modificado a un directorio"


@echo off
setlocal enabledelayedexpansion

REM Este programa BAT copiara en el directorio "copia"

REM el ultimo fichero modificado en el directorio actual

set num=1

for /f "tokens=*" %%a in ('dir /o:-d /a:-d /b') do (

if !num! equ 1 (
echo El archivo modificado mas reciente es: %%a
copy "%%a" copia
dir copia\%%a
set num=2

 El usuario Fernando me preguntó:

"Quiero hacer un archivo BAT que extraiga un caracter de una variable usando un FOR"

Esto no lo puedes hacer con un for.

En los lenguajes de programación como Pascal, Java, C, etc. , existen funciones específicas que sirven
para extraer una subcadena que se encuentre dentro de una cadena de caracteres, pero en la
programación de archivos BAT no hay ningún comando que haga esto directamente (o por lo menos yo no
lo conozco).

Se puede hacer una especie de bucle (usando etiquetas e instrucciones goto) que vayan pasando por el
primer caracter de una variable ( usando " echo %variable:~0,1% " ) y lo vayamos desplazando un lugar
hacia la izquierda ( usando " set variable=%variable:~1% " ). Así haremos que se vayan imprimiendo por
pantalla todos los caracteres de la variable, linea por linea.

@echo off
setlocal enabledelayedexpansion

set /p frase="Escriba una frase: "


if "%frase%" equ "" goto fin

set letra=%frase:~0,1%
set frase=%frase:~1%

goto bucle


 El usuario Mauricio Espinosa Bustos me preguntó:

"Quiero hacer un archivo BAT que elija el archivo de mayor tamaño de un mes y un año concretos, cuyo
nombre empiece por TARRO.D[fecha]"

Supongo que la fecha tiene este formato: dd-mm-aaaa


@echo off
setlocal enabledelayedexpansion

REM Este programa BAT mostrará cual es el archivo mayor

REM del directorio actual, que empieza por TARRO.D[fecha]

set /p mes="Indica el mes con dos digitos : "

set /p anio="Indica el anio con 4 digitos : "

set num=1

for /f "tokens=*" %%a in ('dir /o:-s /a:-d /b "TARRO.D*-%mes%-%anio%"') do (

if !num! equ 1 (
echo El archivo de mayor tamanio del mes %mes% es: %%a
set num=2

 Mi amigo y compañero Carlos Trujillo me comentó el otro día :

"Quiero hacer un archivo BAT que me cree automaticamente carpetas para cada dia del mes de un
determinado año, para guardar las fotos que voy haciendo en ellas, clasificadas por días"


@echo off
setlocal enabledelayedexpansion
set /p mes="Escribe el mes : "
set /p anio="Escribe el anio : "

REM Voy a preguntar cuantos dias tiene el mes

REM para saber cuantas carpetas tengo que hacer

set /p dias="Cuantos dias tiene este mes? :"

for /L %%d in ( 1 , 1 , %dias% ) do (

mkdir "fotos %%d-%mes%-%anio%"

 El usuraio Alcides Dominguez García preguntó :

"Tengo el archivo "datos.txt" que es como el siguiente:

86134 31965 01706 10304 20243 30011 40096 53006 61254 70500 333 10377 55103=
86170 32970 01604 10286 20203 39988 40089 53003=
86185 NIL=
86192 32970 00000 10282 20183 39895 40109 63204 52004=
86210 32570 11802 10302 20225 39769 40103 52020 81200 333 10374 55110
86218 31960 01504 10284 20219 30021 40116 53005 70400

en el que cada linea termina en el caracter "=". Quiero obtener aquellos valores de cada linea que
empiecen por un numero concreto"


@echo off
setlocal enabledelayedexpansion

REM Voy a pedir al usuario que me diga que numeros quiere

REM ver por pantalla, dependiendo de su primer digito

set /p num=Por que digito empiezan los numeros que estas buscando? (0-9) :

REM voy a ir extrayendo linea por linea del archivo

REM "datos.txt" teniendo en cuenta que el caracter "="
REM marca el final de una linea

for /f "tokens=1 delims==" %%a in (datos.txt) do (

set linea=%%a

REM ahora voy recorriendo cada dato de la linea extraida

for %%b in (!linea!) do (

set dato=%%b

REM comparo el primer digito de la variable "dato" con

REM el criterio de busqueda que le indique al principio
REM y si coinciden lo muestro por pantalla

if "!dato:~0,1!" == "!num!" echo !dato!


 El usuraio Anonimo preguntó :

"Tengo el archivo "datos2.txt" que es como el siguiente:

000001 texto
000003 texto
000004 texto
000007 texto

en el que cada linea empieza por un numero de 6 digitos rellenado con ceros a la izquierda. Los numeros
de esas lineas no son correlativos. ¿Como podría corregirlos automaticamente para que si lo fueran?"


@echo off
setlocal enabledelayedexpansion

set relleno=000000
set num=1

echo. > temporal.txt

REM recorro linea por linea el archivo "datos2.txt", le

REM quito el numero de 6 cifras que tiene al principio
REM para despues ponerle otro numero de 6 cifras
REM correctamente numerado, empezando por el 000001

for /f "tokens=1,*" %%a in (datos2.txt) do (

set num2=!relleno!!num!
set num2=!num2:~-6!
echo !num2! %%b >> temporal.txt
set /a num=!num!+1

REM Ahora sustituyo el archivo temporal.txt por datos2.txt

type temporal.txt > datos2.txt

del temporal.txt


 El usuraio Anonimo preguntó :

"Tengo varios archivos de texto, que contienen información. Quiero extraer automaticamente de cada uno
de ellos las lineas que están entre las cadenas de caracteres "DISPENSER ERROR" y "RCLen".
He creado un archivo bat para extraer esas lineas de un archivo llamado "datos2.txt", aqui está la


@echo off
setlocal enabledelayedexpansion

REM con el comando "find /n" voy a buscar la linea en la que

REM aparece la cadena de caracteres "DISPENSER ERROR", junto
REM con su numero de linea. Lo guardo en una variable
REM llamada inicio
for /f %%a in ('find /n "DISPENSER ERROR" datos2.txt ^| more

+2') do (
set inicio=%%a

REM ahora busco el numero de linea en la que aparece "RCLen"

REM y lo guardo en la variable "fin"
for /f %%a in ('find /n "RCLen" datos2.txt ^| more +2') do (
set fin=%%a

REM ahora necesito extraer el numero que se encuentra entre

REM los Corchetes [45], de la variable inicio, porque en
REM esa linea se encuentran "DISPENSER ERROR". Para ello
REM voy a hacer el siguiente bucle

set inicio=!inicio:~1!
set ini=


if "!inicio:~0,1!" == "]" goto :continuar

set ini=!ini!!inicio:~0,1!
set inicio=!inicio:~1!
goto bucle1


REM ahora extraigo el numero de linea en el que se encuentra

REM la cadena RCLen, con el siguiente bucle

set fin=!fin:~1!
set final=

if "!fin:~0,1!" == "]" goto :continuar2

set final=!final!!fin:~0,1!
set fin=!fin:~1!
goto bucle2


REM ahora en las variables "ini" y "final" tengo los numeros

REM de las lineas que debo mostrar del archivo datos2.txt

set num=1

for /f "tokens=*" %%a in (datos2.txt) do (

if !num! geq !ini! (
if !num! leq !final! (
echo %%a
set /a num=!num!+1