232
votes

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?)

0

91 Answers 91

185
votes

Line continuation:

call C:\WINDOWS\system32\ntbackup.exe ^ backup ^ /V:yes ^ /R:no ^ /RS:no ^ /HC:off ^ /M normal ^ /L:s ^ @daily.bks ^ /F daily.bkf 
Sign up to request clarification or add additional context in comments.

4 Comments

The ^ is really a quote char. Using it, you can quote < and > so that they do not redirect output. The ^ at the end of a line also allows line continuation.
Could you please explain this little scriptlet?
@furtelwart: This is the same as if you wrote all into one single line: call C:\WINDOWS\system32\ntbackup.exe backup /V:yes /R:no /RS:no /HC:off /M normal /L:s @daily.bks /F daily.bkf. And to understand all the parameters of that line, simply run C:\WINDOWS\system32\ntbackup.exe /?.
The in depth discussion about this topic, at long commands split over multiple lines
150
votes
PUSHD path 

Takes you to the directory specified by path.

POPD 

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

7 Comments

This also works as a full stack, so you can push many directories onto the stack, and then keep on popping to get back where you were.
Run 'cmd.exe' then type 'help', then type 'help pushd' or 'pushd /?'.
If you pushd to a UNC path, it will automatically map a drive for you and the popd will unmap it.
in csh, if I recall, a pushd with no arg just cycled the stack. But that doesn't work on cmd.exe, does it?
Unlike cd, you can also use pushd to switch between directories on different drives. (If you're in C:\Windows and want to be in D:\Games, then pushd D:\Games takes you there instead of typing D: and cd D:\Games.)
|
109
votes

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.

14 Comments

well, I use it too. I have a "open.cmd" file in one of the PATH directories, and the only thing in that file is "@start ." ;)
'explorer' also does the same thing as 'start' C:\some_directory>explorer .
I tend to type this as [ start "" . ] (brackets for clarity) because start is sometimes finicky about the first param being a title.
If you happen to be on a Mac, open . does the same thing.
start does way more than just open the current folder. You can pass it any file and it will open it with the configured viewer. Give it a URL and your default browser opens, etc...
|
87
votes

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 

4 Comments

I heard :: is more efficient than REM because REM attempts to do environment variable expansion on the stuff that occurs after it, but :: does not.
In fact, :: is just a label with a funny name; therefor, :: will not work if you use it inside a block (in parentheses) since labels are not allowed there either. REM works there of course.
Note though that rem is a documented keyword while :: is just an implementation detail. While it's unlikely that :: will stop working it's generally advisable not to rely on undocumented behavior.
You can even use goto : to jump to the label. :-) and goto -) will also work.
79
votes

Variable substrings:

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

4 Comments

@furtelwart sounds like that could be the batch motto
this great for formatting dates (however the problem I found was that vista/7 and xp output DATE differently)
Note that unfortunately, this cannot be combined with the local variables of FOR loops (for %a in ...) since they don't require the closing percentage sign as does environment variables; you must first assign them to an environment variable (using delayed expansion!) and then extract a substring.
This is an inevitable ugly if you want to do something with dates (e.g. backup file names). After witnessing the general ugliness of Windows command line, I see why Windows is mostly point & click :) Though they say that the new PowerShell is better.
72
votes

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...

5 Comments

I've found the batch files' FOR loops limited and terrible to write, but they are useful sometimes.
Excuse my bafflement, but how on earth is this underused? I think if you don't know FOR loops, you don't know batch scripting.
Underused or not, it is torture. (Some would argue a necessary evil.)
I had to use it a few times and the person who made up this syntax should be fired. From a cannon. Into the sun. It is THAT bad.
@CodingWithStyle: Every time I need to write a for loop in a batch file, the script becomes a bash launcher and rewrite the script in bash (or python) instead : )
60
votes

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.

2 Comments

Would be even cooler if you checked the %1 for "/?" and then you could echo that section as help text.
Hmm, literate batch programming ?!
54
votes

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

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

6 Comments

I usually use %CD% for this. Maybe it is not available in all DOS shell versions?
%CD% is the current directory while %~dp0 is the directory where the running script is located.
Also, I don't think %CD% existed before...XP, maybe. I know some older versions of Windows don't have it.
You should use cd /d %BAT_HOME% instead, if the bat is in another drive. If I remember correctly, this wont work with older DOSes, though.
cd or pushd %BATH_HOME% will not work if you run a batch on a network path.
|
49
votes

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 

Comments

43
votes

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.

6 Comments

You should rather use goto :eof instead of exit /b, does the same thing but is the more standard way to do it.
There's a standard for this? O_o
My mistake. I thought you meant explicitly defining an :eof label and doing a goto to that. I did not realize there is an implicit :eof label at the end of every batch file.
However, if you want a subroutine to set an errorlevel, you will need to use exit /b. For example: exit /b 3
I've found it best to use "exit /B" instead of "goto :eof" to return from a subroutine, "goto :eof" has the problem that you may return an error code when you want to swallow it. For example if you use "if exist someFile echo it's here", this will set the errorlevel if someFile doesn't exist, but that's not wrong, and isn't an error code that you'd want to return (which is what "goto :eof" would do).
|
42
votes

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 

3 Comments

Even better is to put this in a file like sleep.bat to save you the trouble of rewriting it over and over.
...and put the sleep.bat in some directory in the PATH environment variable
I'm against putting this outside, makes the less portable... across Windows systems.
37
votes

Escaping the "plumbing":

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

4 Comments

I'll bet the DOS escape character is not well known. Good one.
Ah, that'd explain why it's the line continuation operator as well -- it escapes the newline, just like \ in bash...
@leander: Yes, but it only can escape the LF (and not the CR), because all CRs are removed before
So this is why using git on Windows to diff against the previous commit (HEAD^) is problematic.
31
votes

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 

3 Comments

Doesn't work with filenames which has spaces in their names... This works: for /f "tokens=*" %i in ('dir /on /b *.jpg') do echo --^> %i
Good catch. Personally I think spaces in file names are evil hideous things from the depths of the ninth circle of Hell. But we should cater for them, I guess.
That's the craziest syntax I've seen yet. Does it work if you made this backtick.bat and pass in the string?
30
votes

Creating an empty file:

> copy nul filename.ext 

4 Comments

@devio: echo. puts an empty line. so the file wouldn't be empty!
I use type nul > filename.ext for that.
Nice I was wondering if there was a touch equivalent for a long time.
I’ve always used ren > blah.txt
28
votes

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.

6 Comments

Do you know how this works? What's the meaning of the 2>&1 bit?
The >nul redirects STDOUT to nul. The 2>&1 redirects STDERR to wherever STDOUT is pointing.
Specifically, I think the 2>&1 is a "filehandle clone", setting STDERR to a clone of STDOUT. Putting it after the >NUL is important, because you want to clone STDOUT after it has been redirected, not before. (Please correct me if I'm wrong here.)
For those of you wondering where PSKILL comes from, check out sysinternals.com. If you're on a Pro edition, you should have a native TSKILL command that's more or less the same.
If you don't have pskill or tskill, most Windows systems I've used come with taskkill.
|
25
votes
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.

8 Comments

I would hardly call this "underused" as I tag it on to the end of every script I write. Then again, it doesn't work so well when you want to have your IDE run the script and capture the output, as the IDE has no way to press enter for you usually...
One neat feature of "pause" is that if there's no terminal around to receive an "any key" (e.g. if your batch file is run from a system service), it detects this and just keeps going...
+1 to Charlie Somerville, this is so known every game programmer's 'go.bat' used it back in the early 90s.
Instead of polluting all of your batch files (and making them annoying to use for CLI geeks), you could use Start / Run / then type 'cmd /k ' and the batch file name. OR change HKCR\batfile\shell\open\command default string to 'cmd /k "%1" %*'. OR make another batchfile which just runs '@cmd /k $*', put it on the desktop and drop your other batch files on it. There are lots of alternatives to PAUSE. Please consider them.
@gnud: you can still pipe to the script: echo.|batch-with-pause.bat will have the same effect as pressing the 'any-key'...
|
25
votes

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.

Comments

18
votes

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% 

Comments

17
votes

Integer arithmetic:

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

4 Comments

How long have SET got the ability to calculate? Windows XP?
If you are asking how big the values can be, I believe this is 32-bit. So +/- 2 billion.
I think the question was, how long has SET been able to calculate? Since Windows XP?
I thing CMD.EXE's SET has been able to calculate since NT 3.1 or so. It just took a long time for anyone to notice that CMD.EXE wasn't exactly the same as COMMAND.COM...
16
votes

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.

Comments

15
votes

Output a blank line:

echo. 

Comments

15
votes

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

if foo if bar baz 

1 Comment

This is really just a shortcut for nesting ifs: if foo ( if bar ( baz ) ) (Imagine newlines after the brackets.)
14
votes

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.

2 Comments

That's an ANSI DOS file - ASCII is 7bit/char.
Lol, this one is an anti-pattern, this conversion is high likely to fail. The only 8 bit encoding supporting Unicode being the UTF-8 but thanks to M$ you cannot have the UTF-8 codepage set on Windows.
14
votes

if block structure:

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

3 Comments

As long as you're aware that variables will be expanded all in one go (without delayed expansion) - i.e. you can't sensibly use %ERRORLEVEL% in there.
You also can't use labels there (which includes those :: style comments) because it will prematurely end the if statement.
@Duncan: You shouldn't use the pseudo-variable %ERRORLEVEL% anyway; that's what if errorlevel <foo> is for. And that does in fact work in such blocks.
12
votes

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 

Comments

12
votes

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 

2 Comments

Interesting. Although thereafter you apparently lose the regular feature, which is to show the currently running command. Is there any way to reset that?
technet.microsoft.com/en-us/library/bb491017.aspx says it can be reset with "title" on its own, but this doesn't seem to work on Windows 7...
11
votes

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.

Comments

11
votes

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 ?

2 Comments

edited; the _'s were interpreted as italics. Nice bit of code.
@Duncan Smart: not true, also works in UK English (although technically it should be "colour", grrr)
10
votes

Total control over output with spacing and escape characters.:

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

3 Comments

How does that work? The dot demarcates the beginning of the text output?
"echo. x" will output "<space>x", "echo x" will only output "x". This allows leading spaces. In addition the "^" escape character will prevent cmd from thinking all those "<" and ">" characters are I/O redirection.
echo( is better, because echo. creates a file search for a file named "echo" if this file exists the echo. fails, if not then internal echo is executed, but it is slower than echo(
9
votes

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 

3 Comments

Surely DATE /T returns 29/10/2008 in Europe and 10/29/2008 in the US... so some localisation may be required! ;-)
That's right! But you can abuse the date command to find out which date format is used.
It's excessive use of FOR, imo. I think I would just use %DATE:~10,4%%DATE:~4,2%%DATE:~7,2% for that rather than run a date command then parse it through for.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.