views:

8695

answers:

72

I think everyone agrees that DOS batch scripting is lame. Nonetheless there are many situations where it is expedient, or you must maintain existing scripts.

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

+42  A: 

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
Chris Noe
According to the principle of greatest astonishment, REM is a command which does nothing, but set errorlevel to 0.
JesperE
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.
Scott Langham
hah! batch files... efficient? ;-)
Duncan Smart
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.
mihi
@Chris, would you mind answering an unrelated question...do you like languages like C++/C# etc more than languages like basic/pascal etc?
SDX2000
:: aka NEKUDOTAYIM PAAMAYIM
LiraNuna
+27  A: 

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

set BAT_HOME=%~dp0
echo %BAT_HOME%
cd %BAT_HOME%
RealHowTo
I usually use %CD% for this. Maybe it is not available in all DOS shell versions?
Saul Dolgin
%CD% is the current directory while %~dp0 is the directory where the running script is located.
RealHowTo
Also, I don't think %CD% existed before...XP, maybe. I know some older versions of Windows don't have it.
Thomas Owens
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.
ketorin
cd or pushd %BATH_HOME% will not work if you run a batch on a network path.
Benoit
+38  A: 

Variable substrings:

> set str=0123456789
> echo %str:~0,5%
01234
> echo %str:~-5,5%
56789
> echo %str:~3,-3%
3456
Chris Noe
Ugly, but veery usefull!
furtelwart
@furtelwart sounds like that could be the batch motto
rzrgenesys187
this great for formatting dates (however the problem I found was that vista/7 and xp output DATE differently)
Daniel
+63  A: 
PUSHD path

Takes you to the directory specified by path.

POPD

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

raven
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.
Kibbee
Seriously? How have I never heard of this feature before?!
Josh Hinman
Run 'cmd.exe' then type 'help', then type 'help pushd' or 'pushd /?'.
paxdiablo
If you pushd to a UNC path, it will automatically map a drive for you and the popd will unmap it.
Ferruccio
+1 especially for the UNC capability, you should add that to your answer.
Adam Mitz
in csh, if I recall, a pushd with no arg just cycled the stack. But that doesn't work on cmd.exe, does it?
Cheeso
this is looooovely :)
demoncodemonkey
+49  A: 

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
wow i didn't know windows batch had this capability..
Claudiu
I've found the batch files' FOR loops limited and terrible to write, but they are useful sometimes.
ya23
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.
Coding With Style
Underused or not, it is torture. (Some would argue a necessary evil.)
harpo
I think I just threw up in my mouth a little
Evan Plaice
+12  A: 

Integer arithmetic:

> SET /A result=10/3 + 1
4
Chris Noe
How long have SET got the ability to calculate? Windows XP?
chakrit
If you are asking how big the values can be, I believe this is 32-bit. So +/- 2 billion.
Chris Noe
I think the question was, how long has SET been able to calculate? Since Windows XP?
Michael Myers
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...
RBerteig
+21  A: 

Creating an empty file:

> copy nul filename.ext
nzpcmad
echo. > filename.ext
devio
@devio: echo. puts an empty line. so the file wouldn't be empty!
Paulius Maruška
+29  A: 

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.

Ferruccio
You should rather use goto :eof instead of exit /b, does the same thing but is the more standard way to do it.
Philibert Perusse
There's a standard for this? O_o
Paulius Maruška
It's not "more standard", it's more common because most people don't know about the /b option of exit.
Ferruccio
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.
Ferruccio
However, if you want a subroutine to set an errorlevel, you will need to use exit /b. For example: exit /b 3
Chris Noe
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).
Scott Langham
+16  A: 
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.

raven
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...
Nicholas Flynt
So you can't pipe something to the script then? Like the 'yes' of the unix world? (I'm just reading this thread out of curiousity -- I've got nothing to offer :p )
gnud
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...
leander
Not exactly hidden...
Charlie Somerville
+1 to Charlie Somerville, this is so known every game programmer's 'go.bat' used it back in the early 90s.
LiraNuna
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.
system PAUSE
@gnud: you can still pipe to the script: `echo.|batch-with-pause.bat` will have the same effect as pressing the 'any-key'...
pipitas
+8  A: 

Total control over output with spacing and excape characters.:

echo.    ^<resourceDir^>/%basedir%/resources^</resourceDir^>
paxdiablo
How does that work? The dot demarcates the beginning of the text output?
Chris Noe
"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.
paxdiablo
+71  A: 

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
Chris Noe
Thanks, I had no idea about this!
I was looking for this last week! (couldn't remember the character)
chilltemp
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.
Cheeso
Could you please explain this little scriptlet?
furtelwart
@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 /?`.
pipitas
+5  A: 

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
paxdiablo
+2  A: 

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.

raven
If you want just a plain list of files without all the directory info, use DIR /B
Ferruccio
+8  A: 

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
paxdiablo
+22  A: 

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
paxdiablo
Oooo, very nice!
Chris Noe
Even better is to put this in a file like sleep.bat to save you the trouble of rewriting it over and over.
erjiang
...and put the sleep.bat in some directory in the PATH environment variable
Scoregraphic
I'm against putting this outside, makes the less portable... across Windows systems.
Sorin Sbarnea
+7  A: 

Output a blank line:

echo.
paxdiablo
+2  A: 

The subdirectory option on 'remove directory':

rd /s /q junk
paxdiablo
A: 

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

for /l %i in (1,1,10) do echo %i
paxdiablo
+23  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
paxdiablo
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
doekman
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.
paxdiablo
+13  A: 

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.

RealHowTo
That's an ANSI DOS file - ASCII is 7bit/char.
MarkJ
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.
Sorin Sbarnea
+52  A: 

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.

LeopardSkinPillBoxHat
Oooh, that's not bad at all.
paxdiablo
+1 I use that all the time ;o)
Andrew
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 ." ;)
Paulius Maruška
'explorer' also does the same thing as 'start' C:\some_directory>explorer .
Ray Vega
+1 Use it all the time :-) ... CD with tab completion in command prompt then pops up an explorer window with start . :-)
chakrit
I prefer "C:\some_directory> explorer /e,." this will open explorer in Folder View
Justin
I tend to type this as [ start "" . ] (brackets for clarity) because start is sometimes finicky about the first param being a title.
system PAUSE
Is the reverse available -- navigate through explorer to a desired folder, then click something to open a command Window "in" that folder?
Philip Kelley
@Philip - There are various powertool utilities which allow this. Here's an example, but I haven't tried this particular software myself:http://www.petri.co.il/add_command_prompt_here_shortcut_to_windows_explorer.htm
LeopardSkinPillBoxHat
I just type `start.` (No space.)
Coding With Style
+9  A: 

if block structure:

if "%VS90COMNTOOLS%"=="" (
  echo: Visual Studio 2008 is not installed
  exit /b
)
Ferruccio
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.
Duncan Smart
You also can't use labels there (which includes those :: style comments) because it will prematurely end the if statement.
Coding With Style
@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.
Joey
A: 

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.

RWendi
That's not much of a trick, just a recursive goto loop. You might as well skip writing :CODELINE and GOTO:CODELINE and instead just replace GOTO :CODELINE with @"%~0" (@"%~0" starts the batch from the beginning again)
Coding With Style
I think the GOTO is clearer than @"%~0" - if I saw @"%~0" in a batch file I would probably scream WTF? And to be honest @"%~0" is not particularly Googlable either in order to look it up :)
demoncodemonkey
Actually I'd just write @%0 now, as I've learned that %0 is an irregular beast and gives the full expanded file name including spaces wrapped within quotation marks while others don't work that way with spaces (see FOR /? for more info). And Demon, that's your problem - :P. You could also just add a comment like "@REM The %0 represents the batch file's name, causing the batch file to repeat itself."
Coding With Style
+8  A: 

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
remonedo
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.
remonedo
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.
Coding With Style
+17  A: 

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.

aphoria
Scott Langham
aphoria
leander
For those of you wondering where PSKILL comes from, check out http://www.sysinternals.com/. If you're on a Pro edition, you should have a native TSKILL command that's more or less the same.
Coding With Style
+1  A: 

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*").

RealHowTo
+1  A: 

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
PabloG
Look, easier to read with underused "::" comments B:-)
Chris Noe
Looks better! :)
PabloG
REM ftw!, death to :: :-P
Joey
Actually, :: Is a special case of a label, and not a comment. See http://ss64.com/nt/rem.html If your batch file is complex enough for this to cause subtle problems ... it's probably time to switch to another language.
Precipitous
+12  A: 

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

if foo if bar baz
Thanks, I just used this one.
shoover
+6  A: 

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 ?

MoreThanChaos
Yep, I use this one everyday for my build logs
Chris Noe
edited; the _'s were interpreted as italics. Nice bit of code.
Will
Very brittle, only works when locale is US English
Duncan Smart
@Duncan Smart: not true, also works in UK English (although technically it should be "colour", grrr)
demoncodemonkey
+12  A: 

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%
SqlACID
+1  A: 

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

Dean
What does this do that Win+R, ping www.google.com doesn't?
sep332
funny, I sware that didn't work on a pc once. working now though! thanks! The /c is still helpful for things that arent in the default path though...
Dean
Replace the /c with /k and it'll stick around and show you the results.
Mattias Andersson
+2  A: 

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!

Will
You can set this without editing the registry directly. Click on the command prompt icon in the upper-left corner of the window. Select Properties. On the Options tab check the QuickEdit Mode box.
aphoria
Yep, the Properties setting is the humane way to go.@Will, suggest you edit to make that the primary advice... But the regedit approach is still useful for say scripting this.
Chris Noe
use 'alt+space e p' to paste clipboard at caret positiondragging a folder from explorer to cmd window puts directory name at caret position
mr calendar
You can script it with reg add HKCU\Console /v QuickEdit /t REG_DWORD /d 1 /f
Arnshea
I don't like quickedit coz it messes with my mouse behaviour. All I want is to be able to press Ctrl+V to paste! I feel an AHK script bubbling inside me... ;)
demoncodemonkey
+22  A: 

Escaping the "plumbing":

echo ^| ^< ^> ^& ^\ ^^
Chris Noe
I'll bet the DOS escape character is not well known. Good one.
Joshua
Ah, that'd explain why it's the line continuation operator as well -- it escapes the newline, just like \ in bash...
leander
+3  A: 

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

Alin Sfetcu
A: 
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.

Mark Arnott
+18  A: 

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.

Patrick Cuff
nice, nice
Charlie Somerville
Indeed, very nice.
Peter Mortensen
Would be even cooler if you checked the %1 for "/?" and then you could echo that section as help text.
demoncodemonkey
Hmm, literate batch programming ?!
Muhammad Alkarouri
+7  A: 

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
Interesting. Although thereafter you apparently lose the regular feature, which is to show the currently running command. Is there any way to reset that?
Chris Noe
http://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...
Matt
+3  A: 

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
)
Philibert Perusse
SET /P is usually better for this.
Coding With Style
SET /P allows you to ask a question to the user and grab the input. The above is for parsing the std input when a program you do not control calls your batch file and pipe you some text. Suppose we use 'dir' as such a program for the sake of testing, 'dir | set /P data=My data' wont work. However the above trick would successfully parse the stdin passed on to the batch file.
Philibert Perusse
Coding With Style
Philibert Perusse
Coding With Style
Yet, my original comment remains. Can you provide a working example, inside a batch file, where you can actually do something useful with what's been put into T? Say, branch upon the result or print something or SET another variable for further use. More indeed, without calling a 2nd batch file to do the processing.
Philibert Perusse
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.

Philibert Perusse
That's for when you want to actually make code that runs on older windows computers. Most of the time it's only for your own code and even if you do share it nowadays it's a safe assumption their windows is recent enough for SETLOCAL ENABLEEXTENSIONS to work, so this isn't really needed anymore.
Coding With Style
ENABLEEXTENSIONS can be forced disabled. Thus calling SETLOCAL ENABLEEXTENSIONS should be one of the first lines to run anyways. The other two lines add a marginal safety at a very low expense. If you are writing a batch as part of an installer or something that needs bulletproofing this trick easily covers your bottom against it. I am sorry to say that I still have to support product installation on Win95/Win98 systems!
Philibert Perusse
+6  A: 

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
Adam K. Johnson
You can also use delayed expansion using ! instead of % (with the right mixture, though). When dealing with arrays of data I frequently use things like "set foo=!array[%i%]!". The %i% gets expanded immediately while the stuff between ! will be expanded afterwards, using the value %i% had at that time.
Joey
Most PCs don't have delayed expansion enabled by default. If you need your variables global, you usually can't rely on delayed expansion to do the trick. Besides, there are times when you need more than one layer of delayed expansion, and this trick comes in handy then.
Coding With Style
True, this does need delayed expansion, but you can enable it within your script. I am updating my answer now to include that.
Adam K. Johnson
A: 

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

set > test
dir test
del test
some
Yes it works but why would you want to do that?
demoncodemonkey
@demoncodemonkey, the size of the variable area is limited.
BoltBait
A: 

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
Sascha
+9  A: 

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.

doekman
+2  A: 
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 ...
Reuben
Sadly, SHIFT does not effect the value of %*, it only modifies the values of the individual params: %1, %2, etc.
Chris Noe
Right. That's still possibly useful in some cases, but I agree that it's inconsistent...
Reuben
+1  A: 

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)
+16  A: 

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.

paxdiablo
+6  A: 

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:\>
paxdiablo
The `where` program can also do this. I'm not sure if this exists before Vista.
Brian
+5  A: 

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)

matt wilkie
+2  A: 

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.

NicJ
Not sure if CHOICE is standard on Windows XP.
Duncan Smart
It isn't, but it should be.
Coding With Style
+3  A: 

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.

NicJ
+20  A: 

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.

+1 - great info, thanks
bill weaver
+3  A: 

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.

Heh, that takes me back.
Coding With Style
A: 

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 :)
YourComputerHelpZ
You need to add goto :eof between those echo sections.
Chris Noe
"Hmm... You have Internet Explorer" - I lol'd <3
demoncodemonkey
A: 

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.

leander
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=
cookre
Downrated, this isn't a hidden feature; it's a meaningless batch. You also use way too much whitespace, used a homegrown method of extracting time instead of just using %TIME%, made your own random seed for no apparent reason instead of using %RANDOM%, and removed variables by hand instead of just using SETLOCAL and ENDLOCAL.
Coding With Style
A: 
Coding With Style
A: 

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.

Coding With Style
+3  A: 

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.

Coding With Style
+1  A: 

Setting environment variables from a file with SET /P

SET /P SVNVERSION=<ver.tmp
Anton Tykhyy
The problem with that is that only the first line of text turns into the value. It might be usable, but it doesn't seem useful.
Coding With Style
+5  A: 

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.

Coding With Style
+1, excellent trick!
ijprest
A: 

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

CMD /C EXIT number
Coding With Style
A: 

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
Chris Noe
+2  A: 

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.

A: 

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.

sahmeepee
A: 

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.

furtelwart
+4  A: 

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.

batch fool
+4  A: 

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
Andy Morris
A: 

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
Andrei Coscodan
A: 

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.

JUST MY correct OPINION
A: 

List all drives:

fsutil fsinfo drives
Andrei Coscodan
+2  A: 

Get the current day, month and year (local-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%
Andrei Coscodan
Wow, impressive and messy at the same time.
dwj
+2  A: 

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.

ConcernedOfTunbridgeWells