[windows] Hidden features of Windows batch files

What are some of the lesser know, but important and useful features of Windows batch files?

Guidelines:

  • One feature per answer
  • Give both a short description of the feature and an example, not just a link to documentation
  • Limit answers to native funtionality, i.e., does not require additional software, like the Windows Resource Kit

Clarification: We refer here to scripts that are processed by cmd.exe, which is the default on WinNT variants.

(See also: Windows batch files: .bat vs .cmd?)

This question is related to windows batch-file hidden-features

The answer is


I use them as quick shortcuts to commonly used directories. An example file named "sandbox.bat" which lives in a directory in my PATH

EXPLORER "C:\Documents and Settings\myusername\Desktop\sandbox"

Invoking the script is just WIN+R --> sandbox


Don't have an editor handy and need to create a batch file?

copy con test.bat

Just type away the commands, press enter for a new line. Press Ctrl-Z and Enter to close the file.


Recursively search for a string in a directory tree:

findstr /S /C:"string literal" *.*

You can also use regular expressions:

findstr /S /R "^ERROR" *.log

Recursive file search:

dir /S myfile.txt

A handy trick when you want to copy files between branches:

C:\src\branch1\mydir\mydir2\mydir3\mydir4>xcopy %cd:branch1=branch2%\foo*
Overwrite C:\src\branch1\mydir\mydir2\mydir3\mydir4\foo.txt (Yes/No/All)? y
C:\src\branch2\mydir\mydir2\mydir3\mydir4\foo.txt

This uses both the %cd% environment variable, and environment variable substitution.


Subroutines (outputs 42):

    @echo off
    call :answer 42
    goto :eof
:do_something
    echo %1
    goto :eof

and subroutines returning a value (outputs 0, 1, 2, and so on):

    @echo off
    setlocal enableextensions enabledelayedexpansion
    call :seq_init seq1
:loop1
    if not %seq1%== 10 (
        call :seq_next seq1
        echo !seq1!
        goto :loop1
    )
    endlocal
    goto :eof

:seq_init
    set /a "%1 = -1"
    goto :eof
:seq_next
    set /a "seq_next_tmp1 = %1"
    set /a "%1 = %seq_next_tmp1% + 1"
    set seq_next_tmp1=
    goto :eof

Multiple commands in one line, useful in many situations:

& Used to combine two commands, executes command1 and then command2
&& A conditional combination, executes command2 if command1 completes successfully
¦¦ Command2 executes only if command1 does not complete successfully.

Examples:

:: ** Edit the most recent .TXT file and exit, useful in a .CMD / .BAT **
FOR /F %%I IN ('DIR *.TXT /B /O:-N') DO NOTEPAD %%I & EXIT


:: ** If exist any .TXT file, display the list in NOTEPAD, if not it 
:: ** exits without any error (note the && and the 2> error redirection)
DIR *.TXT > TXT.LST 2> NUL && NOTEPAD TXT.LST

Creating an empty file:

> copy nul filename.ext

A method to set the errorlevel to any number you desire:

CMD /C EXIT number

Integer arithmetic:

> SET /A result=10/3 + 1
4

The CHOICE command prompts the user for one of multiple options (via a single keypress)

@echo off
echo Please choose one of the following options
echo 1. Apple
echo 2. Orange
echo 3. Pizza
echo a, b, c. Something else
choice /c:123abc /m "Answer?"
set ChoiceLevel=%ErrorLevel%
echo Choice was: %ChoiceLevel%

%ChoiceLevel% will be the nth option selected (in the above example, b=5).

More details at the CHOICE reference page on SS64.com.


A very old (ca 1990) trick to get the total size of the environment variables:

set > test
dir test
del test

The equivalent of the bash (and other shells)

echo -n Hello # or
echo Hello\\c

which outputs "Hello" without a trailing newline. A cmd hack to do this:

<nul set /p any-variable-name=Hello

set /p is a way to prompt the user for input. It emits the given string and then waits, (on the same line, i.e., no CRLF), for the user to type a response.

<nul simply pipes an empty response to the set /p command, so the net result is the emitted prompt string. (The variable used remains unchanged due to the empty reponse.)

Problems are: It's not possible to output a leading equal sign, and on Vista leading whitespace characters are removed, but not on XP.


You can use call to evaluate names later, leading to some useful properties.

call set SomeEnvVariable_%extension%=%%%somevalue%%%

Using call to set variables whose names depend on other variables. If used with some variable naming rules, you can emulate data collections like arrays or dictionaries by using careful naming rules. The triple %'s around somevalue are so it will evaluate to one variable name surrounded by single %'s after the call and before set is invoked. This means two %'s in a row escape down to a single % character, and then it will expand it again, so somevalue is effectively a name pointer.

call set TempVar=%%SomeEnvVariable_%extension%%%

Using it with a temp variable to retrieve the value, which you can then use in logic. This most useful when used in conjunction with delayed variable expansion.

To use this method properly, delayed variable expansion needs to be enabled. Because it is off by default, it is best to enable it within the script by putting this as one of the first instructions:

setlocal EnableDelayedExpansion

This batch file works both with simple files as well as directories as command line parameters (you can mix them in any order). The loop runs the command ('echo' in this example) on any specified file, if a parameter is a directory it runs the command recursively on each file in it.

@echo off
for /f "delims=" %%f in ('dir %* /a-d /b /s') do echo %%f

Total control over output with spacing and escape characters.:

echo.    ^<resourceDir^>/%basedir%/resources^</resourceDir^>

I use them as quick shortcuts to commonly used directories. An example file named "sandbox.bat" which lives in a directory in my PATH

EXPLORER "C:\Documents and Settings\myusername\Desktop\sandbox"

Invoking the script is just WIN+R --> sandbox


I would say DEBUG.EXE is a VERY useful and VERY underused feature of batch files.

The DEBUG command allows you to...

  1. Assemble and disassemble 16-bit code
  2. Read/write memory (Modern protected memory makes this considerably less useful.)
  3. Read data sectors from the hard drive, raw
  4. Hex edit

In short, this tool is EXTREMELY powerful. It might not be used much these days anymore, but the power to call up and control this functionality from a batch script adds a staggering amount of power to batch scripting.

NOTE: Microsoft has removed this command from 64 bit editions of Windows Xp and Vista and intends to remove it from Windows 7 altogether, from what I've heard.


The subdirectory option on 'remove directory':

rd /s /q junk

You can use call to evaluate names later, leading to some useful properties.

call set SomeEnvVariable_%extension%=%%%somevalue%%%

Using call to set variables whose names depend on other variables. If used with some variable naming rules, you can emulate data collections like arrays or dictionaries by using careful naming rules. The triple %'s around somevalue are so it will evaluate to one variable name surrounded by single %'s after the call and before set is invoked. This means two %'s in a row escape down to a single % character, and then it will expand it again, so somevalue is effectively a name pointer.

call set TempVar=%%SomeEnvVariable_%extension%%%

Using it with a temp variable to retrieve the value, which you can then use in logic. This most useful when used in conjunction with delayed variable expansion.

To use this method properly, delayed variable expansion needs to be enabled. Because it is off by default, it is best to enable it within the script by putting this as one of the first instructions:

setlocal EnableDelayedExpansion

Remove surrounding quote.

for /f "useback tokens=*" %%a in ('%str%') do set str=%%~a

I recently have to write a batch file that is called by VS prebuild event and I want to pass in the project directory as parameter. In the batch file I need to concatenate the path with nested subfolder name, but first the surrounding quote need to be removed.


Variable substrings:

> set str=0123456789
> echo %str:~0,5%
01234
> echo %str:~-5,5%
56789
> echo %str:~3,-3%
3456

I really like this Windows XP Commands reference, as well as the Syntax link at the top; it covers many of the tips and tricks already found in other answers.


Variable substrings:

> set str=0123456789
> echo %str:~0,5%
01234
> echo %str:~-5,5%
56789
> echo %str:~3,-3%
3456

With regard to using :: instead of REM for comments: be careful! :: is a special case of a CALL label that acts like a comment. When used inside brackets, for instance in a FOR or IF loop, the function will prematurely exit. Very frustrating to debug!

See http://www.ss64.com/nt/rem.html for a full description.

(adding as a new answer instead of a comment to the first mention of this above because I'm not worthy of commeting yet :0)


Total control over output with spacing and escape characters.:

echo.    ^<resourceDir^>/%basedir%/resources^</resourceDir^>

Inline comments using &::.

:: This is my batch file which does stuff.
copy thisstuff thatstuff  &:: We need to make a backup in case we screw up!

:: ... do lots of other stuff

How does this work? It's an ugly hack. The & is the command separator roughly approximating the ; of UNIX shells. The :: is another ugly hack that kinda-sorta emulates a REM statement. The end result is that you execute your command and then you execute a do-nothing command, thus approximating a comment.

This doesn't work in all situations, but it works often enough to be a useful hack.


PAUSE

Stops execution and displays the following prompt:

Press any key to continue . . .

Useful if you want to run a batch by double-clicking it in Windows Explorer and want to actually see the output rather than just a flash of the command window.


A lot of people use GOTO :EOF these days to terminate their batch files, but you can also use EXIT /B for this purpose.

The advantage behind using EXIT /B is that you can add an errorlevel after EXIT /B, and it will exit with that errorlevel.


The path (with drive) where the script is : ~dp0

set BAT_HOME=%~dp0
echo %BAT_HOME%
cd %BAT_HOME%

To quickly convert an Unicode text file (16bit/char) to a ASCII DOS file (8bit/char).

C:\> type unicodeencoded.txt > dosencoded.txt

as a bonus, if possible, characters are correctly mapped.


/c param for the cmd.exe itself, tells it to run and then do these commands.

I used to find myself frequently doing:

win+r, cmd RETURN, ping google.com RETURN

but now I just do:

win+r, cmd /c ping google.com RETURN

much faster. also helpful if you're using pstools and you want to use psexec to do some command line function on the remote machine.

EDIT: /k Works the same, but leaves the prompt open. This might come in handy more often.


HELP

When working with different OS version it's important to know what commands are available natively. Typing HELP at the command prompt shows what commands are available, with a brief description of what they do.

cmd.exe /? 

This will list all the command line parameters for launching a command prompt as well as registry tweaks that change system wide behavior.


Searching for an executable on the path (or other path-like string if necessary):

c:\> for %i in (cmd.exe) do @echo. %~$PATH:i
C:\WINDOWS\system32\cmd.exe

c:\> for %i in (python.exe) do @echo. %~$PATH:i
C:\Python25\python.exe

c:\>

Don't have an editor handy and need to create a batch file?

copy con test.bat

Just type away the commands, press enter for a new line. Press Ctrl-Z and Enter to close the file.


The equivalent of the bash (and other shells)

echo -n Hello # or
echo Hello\\c

which outputs "Hello" without a trailing newline. A cmd hack to do this:

<nul set /p any-variable-name=Hello

set /p is a way to prompt the user for input. It emits the given string and then waits, (on the same line, i.e., no CRLF), for the user to type a response.

<nul simply pipes an empty response to the set /p command, so the net result is the emitted prompt string. (The variable used remains unchanged due to the empty reponse.)

Problems are: It's not possible to output a leading equal sign, and on Vista leading whitespace characters are removed, but not on XP.


Line-based execution

While not a clear benefit in most cases, it can help when trying to update things while they are running. For example:

UpdateSource.bat

copy UpdateSource.bat Current.bat
echo "Hi!"

Current.bat

copy UpdateSource.bat Current.bat

Now, executing Current.bat produces this output.

HI!

Watch out though, the batch execution proceeds by line number. An update like this could end up skipping or moving back a line if the essential lines don't have exactly the same line numbers.


Searching for an executable on the path (or other path-like string if necessary):

c:\> for %i in (cmd.exe) do @echo. %~$PATH:i
C:\WINDOWS\system32\cmd.exe

c:\> for %i in (python.exe) do @echo. %~$PATH:i
C:\Python25\python.exe

c:\>

I find the ease with which you can redirect the output of commands to files extremely useful:

DIR *.txt > tmp.txt
DIR *.exe >> tmp.txt

Single arrow creates, or overwrites the file, double arrow appends to it. Now I can open tmp.txt in my text editor and do all kinds of good stuff.


Get the current day, month and year (locale-independently):

for /f "tokens=1-4 delims=/-. " %%i in ('date /t') do (call :set_date %%i %%j %%k %%l)
goto :end_set_date

:set_date
if ("%1:~0,1%" gtr "9") shift
for /f "skip=1 tokens=2-4 delims=(-)" %%m in ('echo,^|date') do (set %%m=%1&set %%n=%2&set %%o=%3)
goto :eof

:end_set_date

echo day in 'DD' format is %dd%; month in 'MM' format is %mm%; year in 'YYYY' format is %yy%

PUSHD path

Takes you to the directory specified by path.

POPD

Takes you back to the directory you "pushed" from.


Bail on error.

IF "%errorlevel%" NEQ "0" (
   echo "ERROR:  Something broke.  Bailing out."
   exit /B 1
)

here's one trick that I use to run My Nant Build script consecutively without having to click the batch file over and over again.

:CODELINE
NANT.EXE -buildfile:alltargets.build -l:build.log build.product
@pause
GOTO :CODELINE

What will happen is that after your solution finished building, it will be paused. And then if you press any key it will rerun the build script again. Very handy I must say.


A very old (ca 1990) trick to get the total size of the environment variables:

set > test
dir test
del test

FIND as a replacement for grep.
I hacked a little "phonebook" for myself with find. Very usefull:

@echo off
:begin
set /p term=Enter query: 
type phonebookfile.txt |find /i "%term%"
if %errorlevel% == 0 GOTO :choose
echo No entry found
set /p new_entry=Add new entry: 
echo %new_entry% >> phonebookfile.txt 
:choose
set /p action=(q)uit, (n)ew query or (e)dit? [q] 
if "%action%"=="n" GOTO anfang
if "%action%"=="e" (
    notepad phonebookfile.txt
    goto :choose
)

Very fast and effective.


You can use call to evaluate names later, leading to some useful properties.

call set SomeEnvVariable_%extension%=%%%somevalue%%%

Using call to set variables whose names depend on other variables. If used with some variable naming rules, you can emulate data collections like arrays or dictionaries by using careful naming rules. The triple %'s around somevalue are so it will evaluate to one variable name surrounded by single %'s after the call and before set is invoked. This means two %'s in a row escape down to a single % character, and then it will expand it again, so somevalue is effectively a name pointer.

call set TempVar=%%SomeEnvVariable_%extension%%%

Using it with a temp variable to retrieve the value, which you can then use in logic. This most useful when used in conjunction with delayed variable expansion.

To use this method properly, delayed variable expansion needs to be enabled. Because it is off by default, it is best to enable it within the script by putting this as one of the first instructions:

setlocal EnableDelayedExpansion

The equivalent of the bash (and other shells)

echo -n Hello # or
echo Hello\\c

which outputs "Hello" without a trailing newline. A cmd hack to do this:

<nul set /p any-variable-name=Hello

set /p is a way to prompt the user for input. It emits the given string and then waits, (on the same line, i.e., no CRLF), for the user to type a response.

<nul simply pipes an empty response to the set /p command, so the net result is the emitted prompt string. (The variable used remains unchanged due to the empty reponse.)

Problems are: It's not possible to output a leading equal sign, and on Vista leading whitespace characters are removed, but not on XP.


PAUSE

Stops execution and displays the following prompt:

Press any key to continue . . .

Useful if you want to run a batch by double-clicking it in Windows Explorer and want to actually see the output rather than just a flash of the command window.


example of string subtraction on date and time to get file named "YYYY-MM-DD HH:MM:SS.txt"

echo test > "%date:~0,4%-%date:~5,2%-%date:~8,2% %time:~0,2%_%time:~3,2%_%time:~6,2%.txt"

I use color to indicate if my script end up successfully, failed, or need some input by changing color of text and background. It really helps when you have some machine in reach of your view but quite far away

color XY

where X and Y is hex value from 0 to F, where X - background, Y - text, when X = Y color will not change.

color Z

changes text color to 'Z' and sets black background, 'color 0' won't work

for names of colors call

color ?


To set an enivroment variable from the first line of a file, I use this:

rem a.txt contains one line: abc123
set /p DATA=<a.txt
echo data: %DATA%

This will output: abc123


I find the ease with which you can redirect the output of commands to files extremely useful:

DIR *.txt > tmp.txt
DIR *.exe >> tmp.txt

Single arrow creates, or overwrites the file, double arrow appends to it. Now I can open tmp.txt in my text editor and do all kinds of good stuff.


if block structure:

if "%VS90COMNTOOLS%"=="" (
  echo: Visual Studio 2008 is not installed
  exit /b
)

Sneaky trick to wait N seconds (not part of cmd.exe but isn't extra software since it comes with Windows), see the ping line. You need N+1 pings since the first ping goes out without a delay.

    echo %time%
    call :waitfor 5
    echo %time%
    goto :eof
:waitfor
    setlocal
    set /a "t = %1 + 1"
    >nul ping 127.0.0.1 -n %t%
    endlocal
    goto :eof

Here how to build a CLASSPATH by scanning a given directory.

setlocal ENABLEDELAYEDEXPANSION
if defined CLASSPATH (set CLASSPATH=%CLASSPATH%;.) else (set CLASSPATH=.)
FOR /R .\lib %%G IN (*.jar) DO set CLASSPATH=!CLASSPATH!;%%G
Echo The Classpath definition is %CLASSPATH%

works in XP (or better). With W2K, you need to use a couple of BAT files to achieve the same result (see Include all jars in the classpath definition ).

It's not needed for 1.6 since you can specify a wildcard directly in CLASSPATH (ex. -cp ".\lib*").


Much like above, using CALL, EXIT /B, SETLOCAL & ENDLOCAL you can implement functions with local variables and return values.

example:

@echo off

set x=xxxxx
call :fun 10
echo "%x%"
echo "%y%"
exit /b

:fun
setlocal
set /a y=%1 + 1
endlocal & set x=%y%
exit /b

This will print:

"11"
""

The y variable never leaves the local scope, but because of the way CMD resolves a single line at a time, you can extract the value into the x variable in the parent scope.


With regard to using :: instead of REM for comments: be careful! :: is a special case of a CALL label that acts like a comment. When used inside brackets, for instance in a FOR or IF loop, the function will prematurely exit. Very frustrating to debug!

See http://www.ss64.com/nt/rem.html for a full description.

(adding as a new answer instead of a comment to the first mention of this above because I'm not worthy of commeting yet :0)


SHIFT

It's a way to iterate through a variable number of arguments passed into a script (or sub-routine) on the command line. In its simplest usage, it shifts %2 to be %1, %3 to be %2, and so-on. (You can also pass in a parameter to SHIFT to skip multiple arguments.) This makes the command "destructive" (i.e. %1 goes away forever), but it allows you to avoid hard-coding a maximum number of supported arguments.

Here's a short example to process command-line arguments one at a time:

:ParseArgs

if "%1"=="" (
    goto :DoneParsingArgs
)

rem ... do something with %1 ...

shift

goto :ParseArgs


:DoneParsingArgs

rem ...

if block structure:

if "%VS90COMNTOOLS%"=="" (
  echo: Visual Studio 2008 is not installed
  exit /b
)

The %~dp0 piece was mentioned already, but there is actually more to it: the character(s) after the ~ define the information that is extracted.
No letter result in the return of the patch file name
d - returns the drive letter
p - returns the path
s - returns the short path
x - returns the file extension
So if you execute the script test.bat below from the c:\Temp\long dir name\ folder,

@echo off
echo %0
echo %~d0
echo %~p0
echo %~dp0
echo %~x0
echo %~s0
echo %~sp0

you get the following output

test
c:
\Temp\long dir name\
c:\Temp\long dir name\
.bat
c:\Temp\LONGDI~1\test.bat
\Temp\LONGDI~1\

And if a parameter is passed into your script as in
test c:\temp\mysrc\test.cpp
the same manipulations can be done with the %1 variable.

But the result of the expansion of %0 depends on the location!
At the "top level" of the batch it expands to the current batch filename.
In a function (call), it expands to the function name.

@echo off
echo %0
call :test
goto :eof

:test
echo %0
echo %~0
echo %~n0

The output is (the batchfile is started with myBatch.bat )

myBatch.bat
:test
:test
myBatch

For loops with numeric counters (outputs 1 through 10):

for /l %i in (1,1,10) do echo %i

here's one trick that I use to run My Nant Build script consecutively without having to click the batch file over and over again.

:CODELINE
NANT.EXE -buildfile:alltargets.build -l:build.log build.product
@pause
GOTO :CODELINE

What will happen is that after your solution finished building, it will be paused. And then if you press any key it will rerun the build script again. Very handy I must say.


Delayed expansion of variables (with substrings thrown in for good measure):

    @echo off
    setlocal enableextensions enabledelayedexpansion
    set full=/u01/users/pax
:loop1
    if not "!full:~-1!" == "/" (
        set full2=!full:~-1!!full2!
        set full=!full:~,-1!
        goto :loop1
    )
    echo !full!
    endlocal

Creating an empty file:

> copy nul filename.ext

Command separators:

cls & dir
copy a b && echo Success
copy a b || echo Failure

At the 2nd line, the command after && only runs if the first command is successful.

At the 3rd line, the command after || only runs if the first command failed.


Local variables are still parsed for the line that ENDLOCAL uses. This allows for tricks like:

ENDLOCAL & SET MYGLOBAL=%SOMELOCAL% & SET MYOTHERGLOBAL=%SOMEOTHERLOCAL%

This is is a useful way to transmit results to the calling context. Specifically, %SOMELOCAL% goes out of scope as soon as ENDLOCAL completes, but by then %SOMELOCAL% is already expanded, so the MYGLOBAL is assigned in the calling context with the local variable.

For the same reason, if you decide to do:

ENDLOCAL & SET MYLOCAL=%MYLOCAL%

You'll discover your new MYLOCAL variable is actually now around as a regular environment variable instead of the localized variable you may have intended it to be.


Find strings in files in a folder using the pipe '|' command:

dir /b *.* | findstr /f:/ "thepattern"

Integer arithmetic:

> SET /A result=10/3 + 1
4

The equivalent of the bash (and other shells)

echo -n Hello # or
echo Hello\\c

which outputs "Hello" without a trailing newline. A cmd hack to do this:

<nul set /p any-variable-name=Hello

set /p is a way to prompt the user for input. It emits the given string and then waits, (on the same line, i.e., no CRLF), for the user to type a response.

<nul simply pipes an empty response to the set /p command, so the net result is the emitted prompt string. (The variable used remains unchanged due to the empty reponse.)

Problems are: It's not possible to output a leading equal sign, and on Vista leading whitespace characters are removed, but not on XP.


The subdirectory option on 'remove directory':

rd /s /q junk

Not sure how useful this would be in a batch file, but it's a very convenient command to use in the command prompt:

C:\some_directory> start .

This will open up Windows Explorer in the "some_directory" folder.

I have found this a great time-saver.


Sneaky trick to wait N seconds (not part of cmd.exe but isn't extra software since it comes with Windows), see the ping line. You need N+1 pings since the first ping goes out without a delay.

    echo %time%
    call :waitfor 5
    echo %time%
    goto :eof
:waitfor
    setlocal
    set /a "t = %1 + 1"
    >nul ping 127.0.0.1 -n %t%
    endlocal
    goto :eof

Multiple commands in one line, useful in many situations:

& Used to combine two commands, executes command1 and then command2
&& A conditional combination, executes command2 if command1 completes successfully
¦¦ Command2 executes only if command1 does not complete successfully.

Examples:

:: ** Edit the most recent .TXT file and exit, useful in a .CMD / .BAT **
FOR /F %%I IN ('DIR *.TXT /B /O:-N') DO NOTEPAD %%I & EXIT


:: ** If exist any .TXT file, display the list in NOTEPAD, if not it 
:: ** exits without any error (note the && and the 2> error redirection)
DIR *.TXT > TXT.LST 2> NUL && NOTEPAD TXT.LST

Doesn't provide much functionality, but you can use the title command for a couple of uses, like providing status on a long script in the task bar, or just to enhance user feedback.

@title Searching for ...
:: processing search
@title preparing search results
:: data processing

Variable substrings:

> set str=0123456789
> echo %str:~0,5%
01234
> echo %str:~-5,5%
56789
> echo %str:~3,-3%
3456

Much like above, using CALL, EXIT /B, SETLOCAL & ENDLOCAL you can implement functions with local variables and return values.

example:

@echo off

set x=xxxxx
call :fun 10
echo "%x%"
echo "%y%"
exit /b

:fun
setlocal
set /a y=%1 + 1
endlocal & set x=%y%
exit /b

This will print:

"11"
""

The y variable never leaves the local scope, but because of the way CMD resolves a single line at a time, you can extract the value into the x variable in the parent scope.


Hide input for an interactive batch script:

  @echo off

  echo hP1X500P[PZBBBfh#b##fXf-V@`$fPf]f3/f1/5++u5>in.com

  set /p secret_password="Enter password:"<nul

  for /f "tokens=*" %%i in ('in.com') do (set secret_password=%%i)

  del in.com

Output a blank line:

echo.

You can modify a batch file while it is running. For example you can add a forgotten pause to the end of the file while it's running if you wanted to see the results before the batch file quit.

see Changing a batch file when its running

I personally think of this more as a gotcha than a feature.


Find strings in files in a folder using the pipe '|' command:

dir /b *.* | findstr /f:/ "thepattern"

TheSoftwareJedi already mentioned the for command, but I'm going to mention it again as it is very powerful.

The following outputs the current date in the format YYYYMMDD, I use this when generating directories for backups.

for /f "tokens=2-4 delims=/- " %a in ('DATE/T') do echo %c%b%a

A very old (ca 1990) trick to get the total size of the environment variables:

set > test
dir test
del test

/c param for the cmd.exe itself, tells it to run and then do these commands.

I used to find myself frequently doing:

win+r, cmd RETURN, ping google.com RETURN

but now I just do:

win+r, cmd /c ping google.com RETURN

much faster. also helpful if you're using pstools and you want to use psexec to do some command line function on the remote machine.

EDIT: /k Works the same, but leaves the prompt open. This might come in handy more often.


TheSoftwareJedi already mentioned the for command, but I'm going to mention it again as it is very powerful.

The following outputs the current date in the format YYYYMMDD, I use this when generating directories for backups.

for /f "tokens=2-4 delims=/- " %a in ('DATE/T') do echo %c%b%a

Being able to run commands and process the output (like backticks of '$()' in bash).

for /f %i in ('dir /on /b *.jpg') do echo --^> %i

If there are spaces in filenames, use this:

for /f "tokens=*" %i in ('dir /on /b *.jpg') do echo --^> %i

To get the current date / time to use for log files, etc., I use this in my batch files:

for /f "usebackq tokens=1,2,3,4,5,6,7 delims=/:. " %%a in (`echo %DATE% %TIME%`) do set NOW=%%d%%b%%c_%%e%%f%%g
set LOG=output_%NOW%.log

Search and replace when setting environment variables:

> @set fname=%date:/=%

...removes the "/" from a date for use in timestamped file names.

and substrings too...

> @set dayofweek=%fname:~0,3%

This batch file works both with simple files as well as directories as command line parameters (you can mix them in any order). The loop runs the command ('echo' in this example) on any specified file, if a parameter is a directory it runs the command recursively on each file in it.

@echo off
for /f "delims=" %%f in ('dir %* /a-d /b /s') do echo %%f

A lot of people use GOTO :EOF these days to terminate their batch files, but you can also use EXIT /B for this purpose.

The advantage behind using EXIT /B is that you can add an errorlevel after EXIT /B, and it will exit with that errorlevel.


When using command extensions shell options in a script, it is HIGHLY suggested that you do the following trick at the beginning of your scripts.

-- Information pasted from http://www.ss64.com/nt/setlocal.html

SETLOCAL will set an ERRORLEVEL if given an argument. It will be zero if one of the two valid arguments is given and one otherwise.

You can use this in a batch file to determine if command extensions are available, using the following technique:

VERIFY errors 2>nul
SETLOCAL ENABLEEXTENSIONS
IF ERRORLEVEL 1 echo Unable to enable extensions

This works because "VERIFY errors" sets ERRORLEVEL to 1 and then the SETLOCAL will fail to reset the ERRORLEVEL value if extensions are not available (e.g. if the script is running under command.com)

If Command Extensions are permanently disabled then SETLOCAL ENABLEEXTENSIONS will not restore them.


if block structure:

if "%VS90COMNTOOLS%"=="" (
  echo: Visual Studio 2008 is not installed
  exit /b
)

Doskey Macros.

I've long lost the reference for this, but I still think it's a good idea, and worth sharing.

We can merge batch-files and doskey scripts into a single file. This might seem a little overly clever, but it works.

;= @echo off
;= rem Call DOSKEY and use this file as the macrofile
;= %SystemRoot%\system32\doskey /listsize=1000 /macrofile=%0%
;= rem In batch mode, jump to the end of the file
;= goto end

;= Doskey aliases
h=doskey /history

;= File listing enhancements
ls=dir /x $*

;= Directory navigation
up=cd ..
pd=pushd

;= :end
;= rem ******************************************************************
;= rem * EOF - Don't remove the following line.  It clears out the ';' 
;= rem * macro. Were using it because there is no support for comments
;= rem * in a DOSKEY macro file.
;= rem ******************************************************************
;=

It works by defining a fake doskey macro ';' which is gracefully (or silently) ignored when it is interpreted as a batch-file.

I've shortened the version listed here, if you want more, go here.


The FOR command! While I hate writing batch files, I'm thankful for it.

FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do @echo %i %j %k

would parse each line in myfile.txt, ignoring lines that begin with a semicolon, passing the 2nd and 3rd token from each line to the for body, with tokens delimited by commas and/or spaces. Notice the for body statements reference %i to get the 2nd token, %j to get the 3rd token, and %k to get all remaining tokens after the 3rd.

You can also use this to iterate over directories, directory contents, etc...


Rather than litter a script with REM or :: lines, I do the following at the top of each script:

@echo OFF
goto :START

Description of the script.

Usage:
   myscript -parm1|parm2 > result.txt

:START

Note how you can use the pipe and redirection characters without escaping them.


You can use call to evaluate names later, leading to some useful properties.

call set SomeEnvVariable_%extension%=%%%somevalue%%%

Using call to set variables whose names depend on other variables. If used with some variable naming rules, you can emulate data collections like arrays or dictionaries by using careful naming rules. The triple %'s around somevalue are so it will evaluate to one variable name surrounded by single %'s after the call and before set is invoked. This means two %'s in a row escape down to a single % character, and then it will expand it again, so somevalue is effectively a name pointer.

call set TempVar=%%SomeEnvVariable_%extension%%%

Using it with a temp variable to retrieve the value, which you can then use in logic. This most useful when used in conjunction with delayed variable expansion.

To use this method properly, delayed variable expansion needs to be enabled. Because it is off by default, it is best to enable it within the script by putting this as one of the first instructions:

setlocal EnableDelayedExpansion

For parsing stdin from inside a script you need that trick with the FOR and FIND commands:

for /f "tokens=*" %%g in ('find /V ""') do (
     :: do what you want with %%g
     echo %%g
)

Rather than litter a script with REM or :: lines, I do the following at the top of each script:

@echo OFF
goto :START

Description of the script.

Usage:
   myscript -parm1|parm2 > result.txt

:START

Note how you can use the pipe and redirection characters without escaping them.


Creating an empty file:

> copy nul filename.ext

When passing an unknown number of parameters to a batch file, e.g. when several files are dragged and dropped onto the batch file to launch it, you could refer to each parameter variable by name, e.g.

TYPE %1
TYPE %2
TYPE %3
TYPE %4
TYPE %5
...etc

but this gets very messy when you want to check if each parameter exists:

if [%1] NEQ [] (
TYPE %1
)
if [%2] NEQ [] (
TYPE %2
)
if [%3] NEQ [] (
TYPE %3
)
if [%4] NEQ [] (
TYPE %4
)
if [%5] NEQ [] (
TYPE %5
)
...etc

Also, you can only accept a limited number of parameters with this approach.

Instead, try using the SHIFT command:

:loop
IF [%1] NEQ [] (
TYPE %1
) ELSE (
GOTO end
)
SHIFT
GOTO loop
:end

SHIFT will move all the parameters down by one, so %2 becomes %1 and %3 becomes %2 etc.


HELP

When working with different OS version it's important to know what commands are available natively. Typing HELP at the command prompt shows what commands are available, with a brief description of what they do.

cmd.exe /? 

This will list all the command line parameters for launching a command prompt as well as registry tweaks that change system wide behavior.


This batch file works both with simple files as well as directories as command line parameters (you can mix them in any order). The loop runs the command ('echo' in this example) on any specified file, if a parameter is a directory it runs the command recursively on each file in it.

@echo off
for /f "delims=" %%f in ('dir %* /a-d /b /s') do echo %%f

Multiple commands in one line, useful in many situations:

& Used to combine two commands, executes command1 and then command2
&& A conditional combination, executes command2 if command1 completes successfully
¦¦ Command2 executes only if command1 does not complete successfully.

Examples:

:: ** Edit the most recent .TXT file and exit, useful in a .CMD / .BAT **
FOR /F %%I IN ('DIR *.TXT /B /O:-N') DO NOTEPAD %%I & EXIT


:: ** If exist any .TXT file, display the list in NOTEPAD, if not it 
:: ** exits without any error (note the && and the 2> error redirection)
DIR *.TXT > TXT.LST 2> NUL && NOTEPAD TXT.LST

This batch file works both with simple files as well as directories as command line parameters (you can mix them in any order). The loop runs the command ('echo' in this example) on any specified file, if a parameter is a directory it runs the command recursively on each file in it.

@echo off
for /f "delims=" %%f in ('dir %* /a-d /b /s') do echo %%f

The FOR command! While I hate writing batch files, I'm thankful for it.

FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do @echo %i %j %k

would parse each line in myfile.txt, ignoring lines that begin with a semicolon, passing the 2nd and 3rd token from each line to the for body, with tokens delimited by commas and/or spaces. Notice the for body statements reference %i to get the 2nd token, %j to get the 3rd token, and %k to get all remaining tokens after the 3rd.

You can also use this to iterate over directories, directory contents, etc...


When passing an unknown number of parameters to a batch file, e.g. when several files are dragged and dropped onto the batch file to launch it, you could refer to each parameter variable by name, e.g.

TYPE %1
TYPE %2
TYPE %3
TYPE %4
TYPE %5
...etc

but this gets very messy when you want to check if each parameter exists:

if [%1] NEQ [] (
TYPE %1
)
if [%2] NEQ [] (
TYPE %2
)
if [%3] NEQ [] (
TYPE %3
)
if [%4] NEQ [] (
TYPE %4
)
if [%5] NEQ [] (
TYPE %5
)
...etc

Also, you can only accept a limited number of parameters with this approach.

Instead, try using the SHIFT command:

:loop
IF [%1] NEQ [] (
TYPE %1
) ELSE (
GOTO end
)
SHIFT
GOTO loop
:end

SHIFT will move all the parameters down by one, so %2 becomes %1 and %3 becomes %2 etc.


Allows you to change directory based on environment variable without having to specify the '%' directive. If the variable specified does not exist then try the directory name.

@if defined %1 (call cd "%%%1%%") else (call cd %1)

the correct format for loops with numeric variables is

for /l %%i in (startNumber, counter, endNumber) do echo %%i

more details > http://www.ss64.com/nt/for.html


Delayed expansion of variables (with substrings thrown in for good measure):

    @echo off
    setlocal enableextensions enabledelayedexpansion
    set full=/u01/users/pax
:loop1
    if not "!full:~-1!" == "/" (
        set full2=!full:~-1!!full2!
        set full=!full:~,-1!
        goto :loop1
    )
    echo !full!
    endlocal

Allows you to change directory based on environment variable without having to specify the '%' directive. If the variable specified does not exist then try the directory name.

@if defined %1 (call cd "%%%1%%") else (call cd %1)

here's one trick that I use to run My Nant Build script consecutively without having to click the batch file over and over again.

:CODELINE
NANT.EXE -buildfile:alltargets.build -l:build.log build.product
@pause
GOTO :CODELINE

What will happen is that after your solution finished building, it will be paused. And then if you press any key it will rerun the build script again. Very handy I must say.


The path (with drive) where the script is : ~dp0

set BAT_HOME=%~dp0
echo %BAT_HOME%
cd %BAT_HOME%

The subdirectory option on 'remove directory':

rd /s /q junk

Line-based execution

While not a clear benefit in most cases, it can help when trying to update things while they are running. For example:

UpdateSource.bat

copy UpdateSource.bat Current.bat
echo "Hi!"

Current.bat

copy UpdateSource.bat Current.bat

Now, executing Current.bat produces this output.

HI!

Watch out though, the batch execution proceeds by line number. An update like this could end up skipping or moving back a line if the essential lines don't have exactly the same line numbers.


SHIFT

It's a way to iterate through a variable number of arguments passed into a script (or sub-routine) on the command line. In its simplest usage, it shifts %2 to be %1, %3 to be %2, and so-on. (You can also pass in a parameter to SHIFT to skip multiple arguments.) This makes the command "destructive" (i.e. %1 goes away forever), but it allows you to avoid hard-coding a maximum number of supported arguments.

Here's a short example to process command-line arguments one at a time:

:ParseArgs

if "%1"=="" (
    goto :DoneParsingArgs
)

rem ... do something with %1 ...

shift

goto :ParseArgs


:DoneParsingArgs

rem ...

With regard to using :: instead of REM for comments: be careful! :: is a special case of a CALL label that acts like a comment. When used inside brackets, for instance in a FOR or IF loop, the function will prematurely exit. Very frustrating to debug!

See http://www.ss64.com/nt/rem.html for a full description.

(adding as a new answer instead of a comment to the first mention of this above because I'm not worthy of commeting yet :0)


I have always found it difficult to read comments that are marked by a keyword on each line:

REM blah blah blah

Easier to read:

:: blah blah blah

Recursively search for a string in a directory tree:

findstr /S /C:"string literal" *.*

You can also use regular expressions:

findstr /S /R "^ERROR" *.log

Recursive file search:

dir /S myfile.txt

Quick edit mode in cmd.exe is my favorite. This is slightly off topic, but when interacting with the command shell it can be a lifesaver. No, I'm not being hyperbolic--you will only see caret-capitol-v a certain number of times before you die; the more you see, the faster you die.

  1. Open up regedit (caution, not my fault, blue screen, etc)
  2. Go to HKCU/Console
  3. Set QuickEdit to 1

(You can set this from the UI as well, which is probably the better way. See the comments for instructions. Also there's a nice one line script to do this as well.)

Now, to copy, just left-click and drag to select and right click to copy. To paste, just right click.

NO MORE ^V^V^V^V^V^V^V^V^V^V^V^V^V^V!!!

Crap, I think I just killed somebody. Sorry!


Escaping the "plumbing":

echo ^| ^< ^> ^& ^\ ^^

Redirecting output to the console, even if the batch's output is already redirected to a file via the > con syntax.

Example: foo.cmd:

echo a
echo b > con

Calling:

foo.cmd > output.txt

This will result in "a" going to output.txt yet "b" going to the console.


The CHOICE command prompts the user for one of multiple options (via a single keypress)

@echo off
echo Please choose one of the following options
echo 1. Apple
echo 2. Orange
echo 3. Pizza
echo a, b, c. Something else
choice /c:123abc /m "Answer?"
set ChoiceLevel=%ErrorLevel%
echo Choice was: %ChoiceLevel%

%ChoiceLevel% will be the nth option selected (in the above example, b=5).

More details at the CHOICE reference page on SS64.com.


Variable substrings:

> set str=0123456789
> echo %str:~0,5%
01234
> echo %str:~-5,5%
56789
> echo %str:~3,-3%
3456

By using CALL, EXIT /B, SETLOCAL & ENDLOCAL you can implement subroutines with local variables.

example:

@echo off

set x=xxxxx
call :sub 10
echo %x%
exit /b

:sub
setlocal
set /a x=%1 + 1
echo %x%
endlocal
exit /b

This will print

11
xxxxx

even though :sub modifies x.


For what it's worth, this is quite a good online reference for Windows CMD or batch files. I learned a few things I didn't know from it.


Escaping the "plumbing":

echo ^| ^< ^> ^& ^\ ^^

There is also the EDLIN command. While it may be an old bastard tool once used for line-based text editing, the fact that it's controllable from the command line makes it rather useful for batch scripting, mostly because, just like any other case you'd be using EDLIN, it's the only tool available. After all, EDLIN is not a tool you would ordinarily want to use for text editing, unless you are somewhat masochistic. To quote Tim Patterson (the fellow who wrote it): "I was aghast when I heard that IBM was using it and not throwing it out the window."

NOTE: EDLIN adds old-fashioned EOF (1A) markers to files it edits. If you need to remove them, you'll probably have to use DEBUG.


I find the ease with which you can redirect the output of commands to files extremely useful:

DIR *.txt > tmp.txt
DIR *.exe >> tmp.txt

Single arrow creates, or overwrites the file, double arrow appends to it. Now I can open tmp.txt in my text editor and do all kinds of good stuff.


Quick edit mode in cmd.exe is my favorite. This is slightly off topic, but when interacting with the command shell it can be a lifesaver. No, I'm not being hyperbolic--you will only see caret-capitol-v a certain number of times before you die; the more you see, the faster you die.

  1. Open up regedit (caution, not my fault, blue screen, etc)
  2. Go to HKCU/Console
  3. Set QuickEdit to 1

(You can set this from the UI as well, which is probably the better way. See the comments for instructions. Also there's a nice one line script to do this as well.)

Now, to copy, just left-click and drag to select and right click to copy. To paste, just right click.

NO MORE ^V^V^V^V^V^V^V^V^V^V^V^V^V^V!!!

Crap, I think I just killed somebody. Sorry!


Using pushd to a UNC path will create a temporary drive mapping (starting with Z and working backward to find the next available letter) and put you in that drive and path. When you popd or exit the command prompt, the temporary mapping is gone.

   C:\>pushd \\yourmom\jukebox

   Z:\>pushd \\yourmom\business

   Y:\>

Also, not so much a batch tip as a command-line environment tip, but when you're working at the commandline with pushd and popd and network shares, it's useful to modify your prompt with the $+ (show pushd stack depth) and $M (show network share path).

   C:\utils>prompt $+$m$p$g

   C:\utils>pushd m:

   +\\yourmom\pub M:\>pushd c:\

   ++c:\>pushd
   M:\
   C:\utils  

   ++c:\>popd

   +\\yourmom\pub M:\>popd

   C:\utils>

By using CALL, EXIT /B, SETLOCAL & ENDLOCAL you can implement subroutines with local variables.

example:

@echo off

set x=xxxxx
call :sub 10
echo %x%
exit /b

:sub
setlocal
set /a x=%1 + 1
echo %x%
endlocal
exit /b

This will print

11
xxxxx

even though :sub modifies x.


I have always found it difficult to read comments that are marked by a keyword on each line:

REM blah blah blah

Easier to read:

:: blah blah blah

List all drives:

fsutil fsinfo drives

Create and start editing a new file

copy con new.txt
This is the contents of my file
^Z

Ctrl+Z sends the ASCII EOF character. This is like heredocs in bash:

cat <<EOF > new.txt
This is the contents of my file
EOF

Delayed expansion of variables (with substrings thrown in for good measure):

    @echo off
    setlocal enableextensions enabledelayedexpansion
    set full=/u01/users/pax
:loop1
    if not "!full:~-1!" == "/" (
        set full2=!full:~-1!!full2!
        set full=!full:~,-1!
        goto :loop1
    )
    echo !full!
    endlocal

PAUSE

Stops execution and displays the following prompt:

Press any key to continue . . .

Useful if you want to run a batch by double-clicking it in Windows Explorer and want to actually see the output rather than just a flash of the command window.


By using CALL, EXIT /B, SETLOCAL & ENDLOCAL you can implement subroutines with local variables.

example:

@echo off

set x=xxxxx
call :sub 10
echo %x%
exit /b

:sub
setlocal
set /a x=%1 + 1
echo %x%
endlocal
exit /b

This will print

11
xxxxx

even though :sub modifies x.


The subdirectory option on 'remove directory':

rd /s /q junk

A very old (ca 1990) trick to get the total size of the environment variables:

set > test
dir test
del test

The goto :eof pasteboard

I add "goto :eof" to end of my scripts as a handy space for code fragments. That way I can quickly copy/paste to and from this area, without having to comment/uncomment.

goto :eof
:: code scraps
call this.bat
call that.bat
set TS=%DATE:~10%%DATE:~4,2%%DATE:~7,2%-%TIME:~0,2%%TIME:~3,2%%TIME:~6%%
for /R C:\temp\ %%G in (*.bak) DO del %%G

To hide all output from a command redirect to >nul 2>&1.

For example, the some command line programs display output even if you redirect to >nul. But, if you redirect the output like the line below, all the output will be suppressed.

PSKILL NOTEPAD >nul 2>&1

EDIT: See Ignoring the output of a command for an explanation of how this works.


PUSHD path

Takes you to the directory specified by path.

POPD

Takes you back to the directory you "pushed" from.


The path (with drive) where the script is : ~dp0

set BAT_HOME=%~dp0
echo %BAT_HOME%
cd %BAT_HOME%

To quickly convert an Unicode text file (16bit/char) to a ASCII DOS file (8bit/char).

C:\> type unicodeencoded.txt > dosencoded.txt

as a bonus, if possible, characters are correctly mapped.


TheSoftwareJedi already mentioned the for command, but I'm going to mention it again as it is very powerful.

The following outputs the current date in the format YYYYMMDD, I use this when generating directories for backups.

for /f "tokens=2-4 delims=/- " %a in ('DATE/T') do echo %c%b%a

When using command extensions shell options in a script, it is HIGHLY suggested that you do the following trick at the beginning of your scripts.

-- Information pasted from http://www.ss64.com/nt/setlocal.html

SETLOCAL will set an ERRORLEVEL if given an argument. It will be zero if one of the two valid arguments is given and one otherwise.

You can use this in a batch file to determine if command extensions are available, using the following technique:

VERIFY errors 2>nul
SETLOCAL ENABLEEXTENSIONS
IF ERRORLEVEL 1 echo Unable to enable extensions

This works because "VERIFY errors" sets ERRORLEVEL to 1 and then the SETLOCAL will fail to reset the ERRORLEVEL value if extensions are not available (e.g. if the script is running under command.com)

If Command Extensions are permanently disabled then SETLOCAL ENABLEEXTENSIONS will not restore them.


Not sure how useful this would be in a batch file, but it's a very convenient command to use in the command prompt:

C:\some_directory> start .

This will open up Windows Explorer in the "some_directory" folder.

I have found this a great time-saver.


Search and replace when setting environment variables:

> @set fname=%date:/=%

...removes the "/" from a date for use in timestamped file names.

and substrings too...

> @set dayofweek=%fname:~0,3%

You can use errorlevel to check if a given program is available on the system (current dir or path) where your batchfile will run. For this to work the program you are testing for must run, exit and set an exit code when it does. In the example I use -? as an arg to myExe, most CLI programs have a similar arg such as -h, --help, -v etc ... this ensures it simply runs and exits leaving or setting errorlevel 0

myExe -? >nul 2>&1 
Set errCode=%errorlevel%
@if %errCode% EQU 0 (
    echo myExe -? does not return an error (exists)
) ELSE (
    echo myExe -? returns an error (does not exist)
)

Yes, you could test errorlevel directly rather than assigning it to errCode but this way you can have commands between the test and the condition and you test the condition repeatedly as needed.


Here how to build a CLASSPATH by scanning a given directory.

setlocal ENABLEDELAYEDEXPANSION
if defined CLASSPATH (set CLASSPATH=%CLASSPATH%;.) else (set CLASSPATH=.)
FOR /R .\lib %%G IN (*.jar) DO set CLASSPATH=!CLASSPATH!;%%G
Echo The Classpath definition is %CLASSPATH%

works in XP (or better). With W2K, you need to use a couple of BAT files to achieve the same result (see Include all jars in the classpath definition ).

It's not needed for 1.6 since you can specify a wildcard directly in CLASSPATH (ex. -cp ".\lib*").


Call Set - Expands Environment variables several levels deep.

Found this at http://ss64.com/nt/call.html#advanced from answer to another SO question Batch file variables initialized in a for loop

set VarName=Param
set Param=This

call set Answer=%%%Varname%%%
Echo %Answer%

gives

set VarName=Param
set Param=This
call set Answer=%Param%
Echo This
This

Quick edit mode in cmd.exe is my favorite. This is slightly off topic, but when interacting with the command shell it can be a lifesaver. No, I'm not being hyperbolic--you will only see caret-capitol-v a certain number of times before you die; the more you see, the faster you die.

  1. Open up regedit (caution, not my fault, blue screen, etc)
  2. Go to HKCU/Console
  3. Set QuickEdit to 1

(You can set this from the UI as well, which is probably the better way. See the comments for instructions. Also there's a nice one line script to do this as well.)

Now, to copy, just left-click and drag to select and right click to copy. To paste, just right click.

NO MORE ^V^V^V^V^V^V^V^V^V^V^V^V^V^V!!!

Crap, I think I just killed somebody. Sorry!


the correct format for loops with numeric variables is

for /l %%i in (startNumber, counter, endNumber) do echo %%i

more details > http://www.ss64.com/nt/for.html


Doskey Macros.

I've long lost the reference for this, but I still think it's a good idea, and worth sharing.

We can merge batch-files and doskey scripts into a single file. This might seem a little overly clever, but it works.

;= @echo off
;= rem Call DOSKEY and use this file as the macrofile
;= %SystemRoot%\system32\doskey /listsize=1000 /macrofile=%0%
;= rem In batch mode, jump to the end of the file
;= goto end

;= Doskey aliases
h=doskey /history

;= File listing enhancements
ls=dir /x $*

;= Directory navigation
up=cd ..
pd=pushd

;= :end
;= rem ******************************************************************
;= rem * EOF - Don't remove the following line.  It clears out the ';' 
;= rem * macro. Were using it because there is no support for comments
;= rem * in a DOSKEY macro file.
;= rem ******************************************************************
;=

It works by defining a fake doskey macro ';' which is gracefully (or silently) ignored when it is interpreted as a batch-file.

I've shortened the version listed here, if you want more, go here.


Sneaky trick to wait N seconds (not part of cmd.exe but isn't extra software since it comes with Windows), see the ping line. You need N+1 pings since the first ping goes out without a delay.

    echo %time%
    call :waitfor 5
    echo %time%
    goto :eof
:waitfor
    setlocal
    set /a "t = %1 + 1"
    >nul ping 127.0.0.1 -n %t%
    endlocal
    goto :eof

forfiles is very useful, for instance, to recursive delete all files older than two days

forfiles /D -2 /P "C:\Temp" /S /C "cmd /c del @path"

Append files using copy:

copy file1.txt+file2.txt+file3.txt append.txt

Also, to set all CLI parameters to a single variable:

SET MSG=%*

This will take every word (or symbol) that is separated by spaces and save it to a single batch file variable. Technically, each parameter is %1, %2, $3, etc., but this SET command uses a wildcard to reference every parameter in stdin.

Batch File:

@SET MSG=%*
@echo %MSG%

Command Line:

C:\test>test.bat Hello World!
Hello World!

The %~dp0 piece was mentioned already, but there is actually more to it: the character(s) after the ~ define the information that is extracted.
No letter result in the return of the patch file name
d - returns the drive letter
p - returns the path
s - returns the short path
x - returns the file extension
So if you execute the script test.bat below from the c:\Temp\long dir name\ folder,

@echo off
echo %0
echo %~d0
echo %~p0
echo %~dp0
echo %~x0
echo %~s0
echo %~sp0

you get the following output

test
c:
\Temp\long dir name\
c:\Temp\long dir name\
.bat
c:\Temp\LONGDI~1\test.bat
\Temp\LONGDI~1\

And if a parameter is passed into your script as in
test c:\temp\mysrc\test.cpp
the same manipulations can be done with the %1 variable.

But the result of the expansion of %0 depends on the location!
At the "top level" of the batch it expands to the current batch filename.
In a function (call), it expands to the function name.

@echo off
echo %0
call :test
goto :eof

:test
echo %0
echo %~0
echo %~n0

The output is (the batchfile is started with myBatch.bat )

myBatch.bat
:test
:test
myBatch

Inline comments using &::.

:: This is my batch file which does stuff.
copy thisstuff thatstuff  &:: We need to make a backup in case we screw up!

:: ... do lots of other stuff

How does this work? It's an ugly hack. The & is the command separator roughly approximating the ; of UNIX shells. The :: is another ugly hack that kinda-sorta emulates a REM statement. The end result is that you execute your command and then you execute a do-nothing command, thus approximating a comment.

This doesn't work in all situations, but it works often enough to be a useful hack.


You can chain if statements to get an effect like a short-circuiting boolean `and'.

if foo if bar baz

To hide all output from a command redirect to >nul 2>&1.

For example, the some command line programs display output even if you redirect to >nul. But, if you redirect the output like the line below, all the output will be suppressed.

PSKILL NOTEPAD >nul 2>&1

EDIT: See Ignoring the output of a command for an explanation of how this works.


if block structure:

if "%VS90COMNTOOLS%"=="" (
  echo: Visual Studio 2008 is not installed
  exit /b
)

Sneaky trick to wait N seconds (not part of cmd.exe but isn't extra software since it comes with Windows), see the ping line. You need N+1 pings since the first ping goes out without a delay.

    echo %time%
    call :waitfor 5
    echo %time%
    goto :eof
:waitfor
    setlocal
    set /a "t = %1 + 1"
    >nul ping 127.0.0.1 -n %t%
    endlocal
    goto :eof

Symbolic links:

mklink /d directorylink ..\realdirectory
mklink filelink realfile

The command is native on Windows Server 2008 and newer, including Vista and Windows 7. (It is also included in some Windows Resource Kits.)


Search and replace when setting environment variables:

> @set fname=%date:/=%

...removes the "/" from a date for use in timestamped file names.

and substrings too...

> @set dayofweek=%fname:~0,3%

forfiles is very useful, for instance, to recursive delete all files older than two days

forfiles /D -2 /P "C:\Temp" /S /C "cmd /c del @path"

The CHOICE command prompts the user for one of multiple options (via a single keypress)

@echo off
echo Please choose one of the following options
echo 1. Apple
echo 2. Orange
echo 3. Pizza
echo a, b, c. Something else
choice /c:123abc /m "Answer?"
set ChoiceLevel=%ErrorLevel%
echo Choice was: %ChoiceLevel%

%ChoiceLevel% will be the nth option selected (in the above example, b=5).

More details at the CHOICE reference page on SS64.com.


HELP

When working with different OS version it's important to know what commands are available natively. Typing HELP at the command prompt shows what commands are available, with a brief description of what they do.

cmd.exe /? 

This will list all the command line parameters for launching a command prompt as well as registry tweaks that change system wide behavior.


For loops with numeric counters (outputs 1 through 10):

for /l %i in (1,1,10) do echo %i

Extract random lines of text

@echo off

:: Get time (alas, it's only HH:MM xM

for /f %%a in ('time /t') do set zD1=%%a



:: Get last digit of MM

set zD2=%zD1:~4,1%



:: Seed the randomizer, if needed

if not defined zNUM1 set /a zNUM1=%zD2%


:: Get a kinda random number

set /a zNUM1=zNUM1 * 214013 + 2531011

set /a zNUM2=zNUM1 ^>^> 16 ^& 0x7fff


:: Pull off the first digit

:: (Last digit would be better, but it's late, and I'm tired)

set zIDX=%zNUM2:~0,1%


:: Map it down to 0-3

set /a zIDX=zIDX/3


:: Finally, we can set do some proper initialization

set /a zIIDX=0

set zLO=

set zLL=""


:: Step through each line in the file, looking for line zIDX

for /f "delims=@" %%a in (c:\lines.txt) do call :zoo  %zIDX%  %%a


:: If line zIDX wasn't found, we'll settle for zee LastLine

if "%zLO%"=="" set zLO=%zLL%

goto awdun


:: See if the current line is line zIDX

:zoo


:: Save string of all parms

set zALL=%*


:: Strip off the first parm (sure hope lines aren't longer than 254 chars)

set zWORDS=%zALL:~2,255%


:: Make this line zee LastLine

set zLL=%zWORDS%


:: If this is the line we're looking for, make it zee LineOut

if {%1}=={%zIIDX%} set zLO=%zWORDS%


:: Keep track of line numbers

set /a zIIDX=%zIIDX% + 1

goto :eof




:awdun

echo ==%zLO%==


:: Be socially responsible

set zALL=

set zD1=

set zD2=

set zIDX=

set zIIDX=

set zLL=

set zLO=

:: But don't mess with seed

::set zNUM1=

set zNUM2=

set zWORDS=

Rather than litter a script with REM or :: lines, I do the following at the top of each script:

@echo OFF
goto :START

Description of the script.

Usage:
   myscript -parm1|parm2 > result.txt

:START

Note how you can use the pipe and redirection characters without escaping them.


Here how to build a CLASSPATH by scanning a given directory.

setlocal ENABLEDELAYEDEXPANSION
if defined CLASSPATH (set CLASSPATH=%CLASSPATH%;.) else (set CLASSPATH=.)
FOR /R .\lib %%G IN (*.jar) DO set CLASSPATH=!CLASSPATH!;%%G
Echo The Classpath definition is %CLASSPATH%

works in XP (or better). With W2K, you need to use a couple of BAT files to achieve the same result (see Include all jars in the classpath definition ).

It's not needed for 1.6 since you can specify a wildcard directly in CLASSPATH (ex. -cp ".\lib*").


The FOR command! While I hate writing batch files, I'm thankful for it.

FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do @echo %i %j %k

would parse each line in myfile.txt, ignoring lines that begin with a semicolon, passing the 2nd and 3rd token from each line to the for body, with tokens delimited by commas and/or spaces. Notice the for body statements reference %i to get the 2nd token, %j to get the 3rd token, and %k to get all remaining tokens after the 3rd.

You can also use this to iterate over directories, directory contents, etc...


Integer arithmetic:

> SET /A result=10/3 + 1
4

Findstr with regular expression support:

findstr "^[0-9].*" c:\windows\system32\drivers\etc\hosts

Output a blank line:

echo.

here's one trick that I use to run My Nant Build script consecutively without having to click the batch file over and over again.

:CODELINE
NANT.EXE -buildfile:alltargets.build -l:build.log build.product
@pause
GOTO :CODELINE

What will happen is that after your solution finished building, it will be paused. And then if you press any key it will rerun the build script again. Very handy I must say.


Rather than litter a script with REM or :: lines, I do the following at the top of each script:

@echo OFF
goto :START

Description of the script.

Usage:
   myscript -parm1|parm2 > result.txt

:START

Note how you can use the pipe and redirection characters without escaping them.


Delayed expansion of variables (with substrings thrown in for good measure):

    @echo off
    setlocal enableextensions enabledelayedexpansion
    set full=/u01/users/pax
:loop1
    if not "!full:~-1!" == "/" (
        set full2=!full:~-1!!full2!
        set full=!full:~,-1!
        goto :loop1
    )
    echo !full!
    endlocal

Setting environment variables from a file with SET /P

SET /P SVNVERSION=<ver.tmp

List all drives:

fsutil fsinfo drives

I find the ease with which you can redirect the output of commands to files extremely useful:

DIR *.txt > tmp.txt
DIR *.exe >> tmp.txt

Single arrow creates, or overwrites the file, double arrow appends to it. Now I can open tmp.txt in my text editor and do all kinds of good stuff.


To set an enivroment variable from the first line of a file, I use this:

rem a.txt contains one line: abc123
set /p DATA=<a.txt
echo data: %DATA%

This will output: abc123


Command separators:

cls & dir
copy a b && echo Success
copy a b || echo Failure

At the 2nd line, the command after && only runs if the first command is successful.

At the 3rd line, the command after || only runs if the first command failed.


To quickly convert an Unicode text file (16bit/char) to a ASCII DOS file (8bit/char).

C:\> type unicodeencoded.txt > dosencoded.txt

as a bonus, if possible, characters are correctly mapped.


Redirecting output to the console, even if the batch's output is already redirected to a file via the > con syntax.

Example: foo.cmd:

echo a
echo b > con

Calling:

foo.cmd > output.txt

This will result in "a" going to output.txt yet "b" going to the console.


You can chain if statements to get an effect like a short-circuiting boolean `and'.

if foo if bar baz

Local variables are still parsed for the line that ENDLOCAL uses. This allows for tricks like:

ENDLOCAL & SET MYGLOBAL=%SOMELOCAL% & SET MYOTHERGLOBAL=%SOMEOTHERLOCAL%

This is is a useful way to transmit results to the calling context. Specifically, %SOMELOCAL% goes out of scope as soon as ENDLOCAL completes, but by then %SOMELOCAL% is already expanded, so the MYGLOBAL is assigned in the calling context with the local variable.

For the same reason, if you decide to do:

ENDLOCAL & SET MYLOCAL=%MYLOCAL%

You'll discover your new MYLOCAL variable is actually now around as a regular environment variable instead of the localized variable you may have intended it to be.


example of string subtraction on date and time to get file named "YYYY-MM-DD HH:MM:SS.txt"

echo test > "%date:~0,4%-%date:~5,2%-%date:~8,2% %time:~0,2%_%time:~3,2%_%time:~6,2%.txt"

I use color to indicate if my script end up successfully, failed, or need some input by changing color of text and background. It really helps when you have some machine in reach of your view but quite far away

color XY

where X and Y is hex value from 0 to F, where X - background, Y - text, when X = Y color will not change.

color Z

changes text color to 'Z' and sets black background, 'color 0' won't work

for names of colors call

color ?


To quickly convert an Unicode text file (16bit/char) to a ASCII DOS file (8bit/char).

C:\> type unicodeencoded.txt > dosencoded.txt

as a bonus, if possible, characters are correctly mapped.


Allows you to change directory based on environment variable without having to specify the '%' directive. If the variable specified does not exist then try the directory name.

@if defined %1 (call cd "%%%1%%") else (call cd %1)

Output a blank line:

echo.

Not sure how useful this would be in a batch file, but it's a very convenient command to use in the command prompt:

C:\some_directory> start .

This will open up Windows Explorer in the "some_directory" folder.

I have found this a great time-saver.


Arrays in batch-files.

Set a value:

set count=1
set var%count%=42

Extract a value at the command-line:

call echo %var%count%%

Extract a value in a batch-file:

call echo %%var%count%%%

Note the extra strafing % signs.

The technique may look a little hairy, but it's quite useful. The above will print the contents of var1 (i.e. 42) as we explained. We could also replace the echo command with a set if we wanted to set some other variable to the value in var1. Meaning the following is a valid assignment at the command line:

call set x=%var%count%%

Then to see the value of va1:

echo %x%

Being able to run commands and process the output (like backticks of '$()' in bash).

for /f %i in ('dir /on /b *.jpg') do echo --^> %i

If there are spaces in filenames, use this:

for /f "tokens=*" %i in ('dir /on /b *.jpg') do echo --^> %i

Findstr with regular expression support:

findstr "^[0-9].*" c:\windows\system32\drivers\etc\hosts

Arrays in batch-files.

Set a value:

set count=1
set var%count%=42

Extract a value at the command-line:

call echo %var%count%%

Extract a value in a batch-file:

call echo %%var%count%%%

Note the extra strafing % signs.

The technique may look a little hairy, but it's quite useful. The above will print the contents of var1 (i.e. 42) as we explained. We could also replace the echo command with a set if we wanted to set some other variable to the value in var1. Meaning the following is a valid assignment at the command line:

call set x=%var%count%%

Then to see the value of va1:

echo %x%

Subroutines (outputs 42):

    @echo off
    call :answer 42
    goto :eof
:do_something
    echo %1
    goto :eof

and subroutines returning a value (outputs 0, 1, 2, and so on):

    @echo off
    setlocal enableextensions enabledelayedexpansion
    call :seq_init seq1
:loop1
    if not %seq1%== 10 (
        call :seq_next seq1
        echo !seq1!
        goto :loop1
    )
    endlocal
    goto :eof

:seq_init
    set /a "%1 = -1"
    goto :eof
:seq_next
    set /a "seq_next_tmp1 = %1"
    set /a "%1 = %seq_next_tmp1% + 1"
    set seq_next_tmp1=
    goto :eof

One of the most common requirements of batch scripting is to log the output generated for later review. Yes, you can redirect the stdout and stderr to a file but then you can't see what is going on unless you tail the log file.

So consider running your batch scripts using a stdout/stderr logging utility like logger which will log the output with a timestamp and you are still able to see the script progress as it happens.

Yet another stdout/stderr logging utility

Yet another stdout/stderr logging utility [2010-08-05]
Copyright (C) 2010 LoRd_MuldeR <[email protected]>
Released under the terms of the GNU General Public License (see License.txt)

Usage:
  logger.exe [logger options] : program.exe [program arguments]
  program.exe [program arguments] | logger.exe [logger options] : -

Options:
  -log <file name>  Name of the log file to create (default: "<program> <time>.log")
  -append           Append to the log file instead of replacing the existing file
  -mode <mode>      Write 'stdout' or 'stderr' or 'both' to log file (default: 'both')
  -format <format>  Format of log file, 'raw' or 'time' or 'full' (default: 'time')
  -filter <filter>  Don't write lines to log file that contain this string
  -invert           Invert filter, i.e. write only lines to log file that match filter
  -ignorecase       Apply filter in a case-insensitive way (default: case-sensitive)
  -nojobctrl        Don't add child process to job object (applies to Win2k and later)
  -noescape         Don't escape double quotes when forwarding command-line arguments
  -silent           Don't print additional information to the console
  -priority <flag>  Change process priority (idle/belownormal/normal/abovenormal/high)
  -inputcp <cpid>   Use the specified codepage for input processing (default: 'utf8')
  -outputcp <cpid>  Use the specified codepage for log file output (default: 'utf8')

For loops with numeric counters (outputs 1 through 10):

for /l %i in (1,1,10) do echo %i

The FOR command! While I hate writing batch files, I'm thankful for it.

FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do @echo %i %j %k

would parse each line in myfile.txt, ignoring lines that begin with a semicolon, passing the 2nd and 3rd token from each line to the for body, with tokens delimited by commas and/or spaces. Notice the for body statements reference %i to get the 2nd token, %j to get the 3rd token, and %k to get all remaining tokens after the 3rd.

You can also use this to iterate over directories, directory contents, etc...


TheSoftwareJedi already mentioned the for command, but I'm going to mention it again as it is very powerful.

The following outputs the current date in the format YYYYMMDD, I use this when generating directories for backups.

for /f "tokens=2-4 delims=/- " %a in ('DATE/T') do echo %c%b%a

Extract random lines of text

@echo off

:: Get time (alas, it's only HH:MM xM

for /f %%a in ('time /t') do set zD1=%%a



:: Get last digit of MM

set zD2=%zD1:~4,1%



:: Seed the randomizer, if needed

if not defined zNUM1 set /a zNUM1=%zD2%


:: Get a kinda random number

set /a zNUM1=zNUM1 * 214013 + 2531011

set /a zNUM2=zNUM1 ^>^> 16 ^& 0x7fff


:: Pull off the first digit

:: (Last digit would be better, but it's late, and I'm tired)

set zIDX=%zNUM2:~0,1%


:: Map it down to 0-3

set /a zIDX=zIDX/3


:: Finally, we can set do some proper initialization

set /a zIIDX=0

set zLO=

set zLL=""


:: Step through each line in the file, looking for line zIDX

for /f "delims=@" %%a in (c:\lines.txt) do call :zoo  %zIDX%  %%a


:: If line zIDX wasn't found, we'll settle for zee LastLine

if "%zLO%"=="" set zLO=%zLL%

goto awdun


:: See if the current line is line zIDX

:zoo


:: Save string of all parms

set zALL=%*


:: Strip off the first parm (sure hope lines aren't longer than 254 chars)

set zWORDS=%zALL:~2,255%


:: Make this line zee LastLine

set zLL=%zWORDS%


:: If this is the line we're looking for, make it zee LineOut

if {%1}=={%zIIDX%} set zLO=%zWORDS%


:: Keep track of line numbers

set /a zIIDX=%zIIDX% + 1

goto :eof




:awdun

echo ==%zLO%==


:: Be socially responsible

set zALL=

set zD1=

set zD2=

set zIDX=

set zIIDX=

set zLL=

set zLO=

:: But don't mess with seed

::set zNUM1=

set zNUM2=

set zWORDS=

Allows you to change directory based on environment variable without having to specify the '%' directive. If the variable specified does not exist then try the directory name.

@if defined %1 (call cd "%%%1%%") else (call cd %1)

By using CALL, EXIT /B, SETLOCAL & ENDLOCAL you can implement subroutines with local variables.

example:

@echo off

set x=xxxxx
call :sub 10
echo %x%
exit /b

:sub
setlocal
set /a x=%1 + 1
echo %x%
endlocal
exit /b

This will print

11
xxxxx

even though :sub modifies x.


To hide all output from a command redirect to >nul 2>&1.

For example, the some command line programs display output even if you redirect to >nul. But, if you redirect the output like the line below, all the output will be suppressed.

PSKILL NOTEPAD >nul 2>&1

EDIT: See Ignoring the output of a command for an explanation of how this works.


For parsing stdin from inside a script you need that trick with the FOR and FIND commands:

for /f "tokens=*" %%g in ('find /V ""') do (
     :: do what you want with %%g
     echo %%g
)

Searching for an executable on the path (or other path-like string if necessary):

c:\> for %i in (cmd.exe) do @echo. %~$PATH:i
C:\WINDOWS\system32\cmd.exe

c:\> for %i in (python.exe) do @echo. %~$PATH:i
C:\Python25\python.exe

c:\>

I have always found it difficult to read comments that are marked by a keyword on each line:

REM blah blah blah

Easier to read:

:: blah blah blah

Integer arithmetic:

> SET /A result=10/3 + 1
4

Command separators:

cls & dir
copy a b && echo Success
copy a b || echo Failure

At the 2nd line, the command after && only runs if the first command is successful.

At the 3rd line, the command after || only runs if the first command failed.


The goto :eof pasteboard

I add "goto :eof" to end of my scripts as a handy space for code fragments. That way I can quickly copy/paste to and from this area, without having to comment/uncomment.

goto :eof
:: code scraps
call this.bat
call that.bat
set TS=%DATE:~10%%DATE:~4,2%%DATE:~7,2%-%TIME:~0,2%%TIME:~3,2%%TIME:~6%%
for /R C:\temp\ %%G in (*.bak) DO del %%G

For parsing stdin from inside a script you need that trick with the FOR and FIND commands:

for /f "tokens=*" %%g in ('find /V ""') do (
     :: do what you want with %%g
     echo %%g
)

When using command extensions shell options in a script, it is HIGHLY suggested that you do the following trick at the beginning of your scripts.

-- Information pasted from http://www.ss64.com/nt/setlocal.html

SETLOCAL will set an ERRORLEVEL if given an argument. It will be zero if one of the two valid arguments is given and one otherwise.

You can use this in a batch file to determine if command extensions are available, using the following technique:

VERIFY errors 2>nul
SETLOCAL ENABLEEXTENSIONS
IF ERRORLEVEL 1 echo Unable to enable extensions

This works because "VERIFY errors" sets ERRORLEVEL to 1 and then the SETLOCAL will fail to reset the ERRORLEVEL value if extensions are not available (e.g. if the script is running under command.com)

If Command Extensions are permanently disabled then SETLOCAL ENABLEEXTENSIONS will not restore them.


example of string subtraction on date and time to get file named "YYYY-MM-DD HH:MM:SS.txt"

echo test > "%date:~0,4%-%date:~5,2%-%date:~8,2% %time:~0,2%_%time:~3,2%_%time:~6,2%.txt"

I use color to indicate if my script end up successfully, failed, or need some input by changing color of text and background. It really helps when you have some machine in reach of your view but quite far away

color XY

where X and Y is hex value from 0 to F, where X - background, Y - text, when X = Y color will not change.

color Z

changes text color to 'Z' and sets black background, 'color 0' won't work

for names of colors call

color ?


Search and replace when setting environment variables:

> @set fname=%date:/=%

...removes the "/" from a date for use in timestamped file names.

and substrings too...

> @set dayofweek=%fname:~0,3%

PAUSE

Stops execution and displays the following prompt:

Press any key to continue . . .

Useful if you want to run a batch by double-clicking it in Windows Explorer and want to actually see the output rather than just a flash of the command window.


the correct format for loops with numeric variables is

for /l %%i in (startNumber, counter, endNumber) do echo %%i

more details > http://www.ss64.com/nt/for.html


Remove surrounding quote.

for /f "useback tokens=*" %%a in ('%str%') do set str=%%~a

I recently have to write a batch file that is called by VS prebuild event and I want to pass in the project directory as parameter. In the batch file I need to concatenate the path with nested subfolder name, but first the surrounding quote need to be removed.


Subroutines (outputs 42):

    @echo off
    call :answer 42
    goto :eof
:do_something
    echo %1
    goto :eof

and subroutines returning a value (outputs 0, 1, 2, and so on):

    @echo off
    setlocal enableextensions enabledelayedexpansion
    call :seq_init seq1
:loop1
    if not %seq1%== 10 (
        call :seq_next seq1
        echo !seq1!
        goto :loop1
    )
    endlocal
    goto :eof

:seq_init
    set /a "%1 = -1"
    goto :eof
:seq_next
    set /a "seq_next_tmp1 = %1"
    set /a "%1 = %seq_next_tmp1% + 1"
    set seq_next_tmp1=
    goto :eof

Searching for an executable on the path (or other path-like string if necessary):

c:\> for %i in (cmd.exe) do @echo. %~$PATH:i
C:\WINDOWS\system32\cmd.exe

c:\> for %i in (python.exe) do @echo. %~$PATH:i
C:\Python25\python.exe

c:\>

example of string subtraction on date and time to get file named "YYYY-MM-DD HH:MM:SS.txt"

echo test > "%date:~0,4%-%date:~5,2%-%date:~8,2% %time:~0,2%_%time:~3,2%_%time:~6,2%.txt"

I use color to indicate if my script end up successfully, failed, or need some input by changing color of text and background. It really helps when you have some machine in reach of your view but quite far away

color XY

where X and Y is hex value from 0 to F, where X - background, Y - text, when X = Y color will not change.

color Z

changes text color to 'Z' and sets black background, 'color 0' won't work

for names of colors call

color ?


Doesn't provide much functionality, but you can use the title command for a couple of uses, like providing status on a long script in the task bar, or just to enhance user feedback.

@title Searching for ...
:: processing search
@title preparing search results
:: data processing

Symbolic links:

mklink /d directorylink ..\realdirectory
mklink filelink realfile

The command is native on Windows Server 2008 and newer, including Vista and Windows 7. (It is also included in some Windows Resource Kits.)


You can use errorlevel to check if a given program is available on the system (current dir or path) where your batchfile will run. For this to work the program you are testing for must run, exit and set an exit code when it does. In the example I use -? as an arg to myExe, most CLI programs have a similar arg such as -h, --help, -v etc ... this ensures it simply runs and exits leaving or setting errorlevel 0

myExe -? >nul 2>&1 
Set errCode=%errorlevel%
@if %errCode% EQU 0 (
    echo myExe -? does not return an error (exists)
) ELSE (
    echo myExe -? returns an error (does not exist)
)

Yes, you could test errorlevel directly rather than assigning it to errCode but this way you can have commands between the test and the condition and you test the condition repeatedly as needed.


The path (with drive) where the script is : ~dp0

set BAT_HOME=%~dp0
echo %BAT_HOME%
cd %BAT_HOME%

Total control over output with spacing and escape characters.:

echo.    ^<resourceDir^>/%basedir%/resources^</resourceDir^>

Doesn't provide much functionality, but you can use the title command for a couple of uses, like providing status on a long script in the task bar, or just to enhance user feedback.

@title Searching for ...
:: processing search
@title preparing search results
:: data processing

FIND as a replacement for grep.
I hacked a little "phonebook" for myself with find. Very usefull:

@echo off
:begin
set /p term=Enter query: 
type phonebookfile.txt |find /i "%term%"
if %errorlevel% == 0 GOTO :choose
echo No entry found
set /p new_entry=Add new entry: 
echo %new_entry% >> phonebookfile.txt 
:choose
set /p action=(q)uit, (n)ew query or (e)dit? [q] 
if "%action%"=="n" GOTO anfang
if "%action%"=="e" (
    notepad phonebookfile.txt
    goto :choose
)

Very fast and effective.


I would say DEBUG.EXE is a VERY useful and VERY underused feature of batch files.

The DEBUG command allows you to...

  1. Assemble and disassemble 16-bit code
  2. Read/write memory (Modern protected memory makes this considerably less useful.)
  3. Read data sectors from the hard drive, raw
  4. Hex edit

In short, this tool is EXTREMELY powerful. It might not be used much these days anymore, but the power to call up and control this functionality from a batch script adds a staggering amount of power to batch scripting.

NOTE: Microsoft has removed this command from 64 bit editions of Windows Xp and Vista and intends to remove it from Windows 7 altogether, from what I've heard.


Output a blank line:

echo.

Create and start editing a new file

copy con new.txt
This is the contents of my file
^Z

Ctrl+Z sends the ASCII EOF character. This is like heredocs in bash:

cat <<EOF > new.txt
This is the contents of my file
EOF

/c param for the cmd.exe itself, tells it to run and then do these commands.

I used to find myself frequently doing:

win+r, cmd RETURN, ping google.com RETURN

but now I just do:

win+r, cmd /c ping google.com RETURN

much faster. also helpful if you're using pstools and you want to use psexec to do some command line function on the remote machine.

EDIT: /k Works the same, but leaves the prompt open. This might come in handy more often.


Command separators:

cls & dir
copy a b && echo Success
copy a b || echo Failure

At the 2nd line, the command after && only runs if the first command is successful.

At the 3rd line, the command after || only runs if the first command failed.


Redirecting output to the console, even if the batch's output is already redirected to a file via the > con syntax.

Example: foo.cmd:

echo a
echo b > con

Calling:

foo.cmd > output.txt

This will result in "a" going to output.txt yet "b" going to the console.


The IF command! Without it my batch file was junk!

@echo off
IF exist %windir%\system32\iexplore.exe goto end

echo Hmm... it seems you do not have Internet Explorer.
echo Great! You seem to understand ;)

:end
echo Hmm... You have Internet Explorer.
echo That is bad :)

PUSHD path

Takes you to the directory specified by path.

POPD

Takes you back to the directory you "pushed" from.


A handy trick when you want to copy files between branches:

C:\src\branch1\mydir\mydir2\mydir3\mydir4>xcopy %cd:branch1=branch2%\foo*
Overwrite C:\src\branch1\mydir\mydir2\mydir3\mydir4\foo.txt (Yes/No/All)? y
C:\src\branch2\mydir\mydir2\mydir3\mydir4\foo.txt

This uses both the %cd% environment variable, and environment variable substitution.


Setting environment variables from a file with SET /P

SET /P SVNVERSION=<ver.tmp

The CHOICE command prompts the user for one of multiple options (via a single keypress)

@echo off
echo Please choose one of the following options
echo 1. Apple
echo 2. Orange
echo 3. Pizza
echo a, b, c. Something else
choice /c:123abc /m "Answer?"
set ChoiceLevel=%ErrorLevel%
echo Choice was: %ChoiceLevel%

%ChoiceLevel% will be the nth option selected (in the above example, b=5).

More details at the CHOICE reference page on SS64.com.


Escaping the "plumbing":

echo ^| ^< ^> ^& ^\ ^^

Not sure how useful this would be in a batch file, but it's a very convenient command to use in the command prompt:

C:\some_directory> start .

This will open up Windows Explorer in the "some_directory" folder.

I have found this a great time-saver.


Doesn't provide much functionality, but you can use the title command for a couple of uses, like providing status on a long script in the task bar, or just to enhance user feedback.

@title Searching for ...
:: processing search
@title preparing search results
:: data processing

To get the current date / time to use for log files, etc., I use this in my batch files:

for /f "usebackq tokens=1,2,3,4,5,6,7 delims=/:. " %%a in (`echo %DATE% %TIME%`) do set NOW=%%d%%b%%c_%%e%%f%%g
set LOG=output_%NOW%.log

You can chain if statements to get an effect like a short-circuiting boolean `and'.

if foo if bar baz

Multiple commands in one line, useful in many situations:

& Used to combine two commands, executes command1 and then command2
&& A conditional combination, executes command2 if command1 completes successfully
¦¦ Command2 executes only if command1 does not complete successfully.

Examples:

:: ** Edit the most recent .TXT file and exit, useful in a .CMD / .BAT **
FOR /F %%I IN ('DIR *.TXT /B /O:-N') DO NOTEPAD %%I & EXIT


:: ** If exist any .TXT file, display the list in NOTEPAD, if not it 
:: ** exits without any error (note the && and the 2> error redirection)
DIR *.TXT > TXT.LST 2> NUL && NOTEPAD TXT.LST

SHIFT

It's a way to iterate through a variable number of arguments passed into a script (or sub-routine) on the command line. In its simplest usage, it shifts %2 to be %1, %3 to be %2, and so-on. (You can also pass in a parameter to SHIFT to skip multiple arguments.) This makes the command "destructive" (i.e. %1 goes away forever), but it allows you to avoid hard-coding a maximum number of supported arguments.

Here's a short example to process command-line arguments one at a time:

:ParseArgs

if "%1"=="" (
    goto :DoneParsingArgs
)

rem ... do something with %1 ...

shift

goto :ParseArgs


:DoneParsingArgs

rem ...

/c param for the cmd.exe itself, tells it to run and then do these commands.

I used to find myself frequently doing:

win+r, cmd RETURN, ping google.com RETURN

but now I just do:

win+r, cmd /c ping google.com RETURN

much faster. also helpful if you're using pstools and you want to use psexec to do some command line function on the remote machine.

EDIT: /k Works the same, but leaves the prompt open. This might come in handy more often.


Subroutines (outputs 42):

    @echo off
    call :answer 42
    goto :eof
:do_something
    echo %1
    goto :eof

and subroutines returning a value (outputs 0, 1, 2, and so on):

    @echo off
    setlocal enableextensions enabledelayedexpansion
    call :seq_init seq1
:loop1
    if not %seq1%== 10 (
        call :seq_next seq1
        echo !seq1!
        goto :loop1
    )
    endlocal
    goto :eof

:seq_init
    set /a "%1 = -1"
    goto :eof
:seq_next
    set /a "seq_next_tmp1 = %1"
    set /a "%1 = %seq_next_tmp1% + 1"
    set seq_next_tmp1=
    goto :eof

Call Set - Expands Environment variables several levels deep.

Found this at http://ss64.com/nt/call.html#advanced from answer to another SO question Batch file variables initialized in a for loop

set VarName=Param
set Param=This

call set Answer=%%%Varname%%%
Echo %Answer%

gives

set VarName=Param
set Param=This
call set Answer=%Param%
Echo This
This

You can chain if statements to get an effect like a short-circuiting boolean `and'.

if foo if bar baz

Here how to build a CLASSPATH by scanning a given directory.

setlocal ENABLEDELAYEDEXPANSION
if defined CLASSPATH (set CLASSPATH=%CLASSPATH%;.) else (set CLASSPATH=.)
FOR /R .\lib %%G IN (*.jar) DO set CLASSPATH=!CLASSPATH!;%%G
Echo The Classpath definition is %CLASSPATH%

works in XP (or better). With W2K, you need to use a couple of BAT files to achieve the same result (see Include all jars in the classpath definition ).

It's not needed for 1.6 since you can specify a wildcard directly in CLASSPATH (ex. -cp ".\lib*").


When using command extensions shell options in a script, it is HIGHLY suggested that you do the following trick at the beginning of your scripts.

-- Information pasted from http://www.ss64.com/nt/setlocal.html

SETLOCAL will set an ERRORLEVEL if given an argument. It will be zero if one of the two valid arguments is given and one otherwise.

You can use this in a batch file to determine if command extensions are available, using the following technique:

VERIFY errors 2>nul
SETLOCAL ENABLEEXTENSIONS
IF ERRORLEVEL 1 echo Unable to enable extensions

This works because "VERIFY errors" sets ERRORLEVEL to 1 and then the SETLOCAL will fail to reset the ERRORLEVEL value if extensions are not available (e.g. if the script is running under command.com)

If Command Extensions are permanently disabled then SETLOCAL ENABLEEXTENSIONS will not restore them.


For parsing stdin from inside a script you need that trick with the FOR and FIND commands:

for /f "tokens=*" %%g in ('find /V ""') do (
     :: do what you want with %%g
     echo %%g
)

Creating an empty file:

> copy nul filename.ext

the correct format for loops with numeric variables is

for /l %%i in (startNumber, counter, endNumber) do echo %%i

more details > http://www.ss64.com/nt/for.html


Using pushd to a UNC path will create a temporary drive mapping (starting with Z and working backward to find the next available letter) and put you in that drive and path. When you popd or exit the command prompt, the temporary mapping is gone.

   C:\>pushd \\yourmom\jukebox

   Z:\>pushd \\yourmom\business

   Y:\>

Also, not so much a batch tip as a command-line environment tip, but when you're working at the commandline with pushd and popd and network shares, it's useful to modify your prompt with the $+ (show pushd stack depth) and $M (show network share path).

   C:\utils>prompt $+$m$p$g

   C:\utils>pushd m:

   +\\yourmom\pub M:\>pushd c:\

   ++c:\>pushd
   M:\
   C:\utils  

   ++c:\>popd

   +\\yourmom\pub M:\>popd

   C:\utils>

Get the current day, month and year (locale-independently):

for /f "tokens=1-4 delims=/-. " %%i in ('date /t') do (call :set_date %%i %%j %%k %%l)
goto :end_set_date

:set_date
if ("%1:~0,1%" gtr "9") shift
for /f "skip=1 tokens=2-4 delims=(-)" %%m in ('echo,^|date') do (set %%m=%1&set %%n=%2&set %%o=%3)
goto :eof

:end_set_date

echo day in 'DD' format is %dd%; month in 'MM' format is %mm%; year in 'YYYY' format is %yy%

I have always found it difficult to read comments that are marked by a keyword on each line:

REM blah blah blah

Easier to read:

:: blah blah blah

Being able to run commands and process the output (like backticks of '$()' in bash).

for /f %i in ('dir /on /b *.jpg') do echo --^> %i

If there are spaces in filenames, use this:

for /f "tokens=*" %i in ('dir /on /b *.jpg') do echo --^> %i

One of the most common requirements of batch scripting is to log the output generated for later review. Yes, you can redirect the stdout and stderr to a file but then you can't see what is going on unless you tail the log file.

So consider running your batch scripts using a stdout/stderr logging utility like logger which will log the output with a timestamp and you are still able to see the script progress as it happens.

Yet another stdout/stderr logging utility

Yet another stdout/stderr logging utility [2010-08-05]
Copyright (C) 2010 LoRd_MuldeR <[email protected]>
Released under the terms of the GNU General Public License (see License.txt)

Usage:
  logger.exe [logger options] : program.exe [program arguments]
  program.exe [program arguments] | logger.exe [logger options] : -

Options:
  -log <file name>  Name of the log file to create (default: "<program> <time>.log")
  -append           Append to the log file instead of replacing the existing file
  -mode <mode>      Write 'stdout' or 'stderr' or 'both' to log file (default: 'both')
  -format <format>  Format of log file, 'raw' or 'time' or 'full' (default: 'time')
  -filter <filter>  Don't write lines to log file that contain this string
  -invert           Invert filter, i.e. write only lines to log file that match filter
  -ignorecase       Apply filter in a case-insensitive way (default: case-sensitive)
  -nojobctrl        Don't add child process to job object (applies to Win2k and later)
  -noescape         Don't escape double quotes when forwarding command-line arguments
  -silent           Don't print additional information to the console
  -priority <flag>  Change process priority (idle/belownormal/normal/abovenormal/high)
  -inputcp <cpid>   Use the specified codepage for input processing (default: 'utf8')
  -outputcp <cpid>  Use the specified codepage for log file output (default: 'utf8')

HELP

When working with different OS version it's important to know what commands are available natively. Typing HELP at the command prompt shows what commands are available, with a brief description of what they do.

cmd.exe /? 

This will list all the command line parameters for launching a command prompt as well as registry tweaks that change system wide behavior.


There is also the EDLIN command. While it may be an old bastard tool once used for line-based text editing, the fact that it's controllable from the command line makes it rather useful for batch scripting, mostly because, just like any other case you'd be using EDLIN, it's the only tool available. After all, EDLIN is not a tool you would ordinarily want to use for text editing, unless you are somewhat masochistic. To quote Tim Patterson (the fellow who wrote it): "I was aghast when I heard that IBM was using it and not throwing it out the window."

NOTE: EDLIN adds old-fashioned EOF (1A) markers to files it edits. If you need to remove them, you'll probably have to use DEBUG.


With regard to using :: instead of REM for comments: be careful! :: is a special case of a CALL label that acts like a comment. When used inside brackets, for instance in a FOR or IF loop, the function will prematurely exit. Very frustrating to debug!

See http://www.ss64.com/nt/rem.html for a full description.

(adding as a new answer instead of a comment to the first mention of this above because I'm not worthy of commeting yet :0)


Bail on error.

IF "%errorlevel%" NEQ "0" (
   echo "ERROR:  Something broke.  Bailing out."
   exit /B 1
)

To hide all output from a command redirect to >nul 2>&1.

For example, the some command line programs display output even if you redirect to >nul. But, if you redirect the output like the line below, all the output will be suppressed.

PSKILL NOTEPAD >nul 2>&1

EDIT: See Ignoring the output of a command for an explanation of how this works.


PUSHD path

Takes you to the directory specified by path.

POPD

Takes you back to the directory you "pushed" from.


You can modify a batch file while it is running. For example you can add a forgotten pause to the end of the file while it's running if you wanted to see the results before the batch file quit.

see Changing a batch file when its running

I personally think of this more as a gotcha than a feature.


Redirecting output to the console, even if the batch's output is already redirected to a file via the > con syntax.

Example: foo.cmd:

echo a
echo b > con

Calling:

foo.cmd > output.txt

This will result in "a" going to output.txt yet "b" going to the console.


Escaping the "plumbing":

echo ^| ^< ^> ^& ^\ ^^

Hide input for an interactive batch script:

  @echo off

  echo hP1X500P[PZBBBfh#b##fXf-V@`$fPf]f3/f1/5++u5>in.com

  set /p secret_password="Enter password:"<nul

  for /f "tokens=*" %%i in ('in.com') do (set secret_password=%%i)

  del in.com

For what it's worth, this is quite a good online reference for Windows CMD or batch files. I learned a few things I didn't know from it.


Being able to run commands and process the output (like backticks of '$()' in bash).

for /f %i in ('dir /on /b *.jpg') do echo --^> %i

If there are spaces in filenames, use this:

for /f "tokens=*" %i in ('dir /on /b *.jpg') do echo --^> %i

The IF command! Without it my batch file was junk!

@echo off
IF exist %windir%\system32\iexplore.exe goto end

echo Hmm... it seems you do not have Internet Explorer.
echo Great! You seem to understand ;)

:end
echo Hmm... You have Internet Explorer.
echo That is bad :)

Quick edit mode in cmd.exe is my favorite. This is slightly off topic, but when interacting with the command shell it can be a lifesaver. No, I'm not being hyperbolic--you will only see caret-capitol-v a certain number of times before you die; the more you see, the faster you die.

  1. Open up regedit (caution, not my fault, blue screen, etc)
  2. Go to HKCU/Console
  3. Set QuickEdit to 1

(You can set this from the UI as well, which is probably the better way. See the comments for instructions. Also there's a nice one line script to do this as well.)

Now, to copy, just left-click and drag to select and right click to copy. To paste, just right click.

NO MORE ^V^V^V^V^V^V^V^V^V^V^V^V^V^V!!!

Crap, I think I just killed somebody. Sorry!


I really like this Windows XP Commands reference, as well as the Syntax link at the top; it covers many of the tips and tricks already found in other answers.


A method to set the errorlevel to any number you desire:

CMD /C EXIT number

Examples related to windows

"Permission Denied" trying to run Python on Windows 10 A fatal error occurred while creating a TLS client credential. The internal error state is 10013 How to install OpenJDK 11 on Windows? I can't install pyaudio on Windows? How to solve "error: Microsoft Visual C++ 14.0 is required."? git clone: Authentication failed for <URL> How to avoid the "Windows Defender SmartScreen prevented an unrecognized app from starting warning" XCOPY: Overwrite all without prompt in BATCH Laravel 5 show ErrorException file_put_contents failed to open stream: No such file or directory how to open Jupyter notebook in chrome on windows Tensorflow import error: No module named 'tensorflow'

Examples related to batch-file

'ls' is not recognized as an internal or external command, operable program or batch file '' is not recognized as an internal or external command, operable program or batch file XCOPY: Overwrite all without prompt in BATCH Can´t run .bat file under windows 10 Execute a batch file on a remote PC using a batch file on local PC Windows batch - concatenate multiple text files into one How do I create a shortcut via command-line in Windows? Getting Error:JRE_HOME variable is not defined correctly when trying to run startup.bat of Apache-Tomcat Curl not recognized as an internal or external command, operable program or batch file Best way to script remote SSH commands in Batch (Windows)

Examples related to hidden-features

Hidden features of Windows batch files Hidden features of Python Hidden Features of C#?