Batch Script Best Practices
Batch scripting rewards defensive habits because the language has few safety nets: variables are untyped strings, whitespace is significant, and a missing quote can silently change what a command does rather than throwing a clear error. The core best practices — always quoting paths, always scoping variables with setlocal, always checking exit codes, and always starting scripts with @echo off plus a header comment — exist specifically to counter these sharp edges rather than as arbitrary style preferences.
Cricket analogy: A batter like Rahul Dravid wearing full protective gear against even a gentle net bowler, not just in a Test match, mirrors always quoting paths and checking exit codes even in 'simple' scripts.
Quoting and Whitespace Discipline
Always wrap paths and variable expansions in double quotes — 'if exist "%FILE%"' rather than 'if exist %FILE%' — because unquoted paths containing spaces silently split into two arguments and produce confusing 'file not found' errors instead of a clear syntax error. Prefer 'set "VAR=value"' with the quote immediately before VAR (not around the whole assignment casually) so that trailing spaces after 'value' aren't accidentally captured into the variable, which is one of the most common sources of mysterious IF comparison failures in batch scripts.
Cricket analogy: A groundskeeper marking the exact crease line with chalk rather than eyeballing it avoids disputes over run-outs, the way quoting "%FILE%" avoids ambiguity over where a path's argument boundary actually is.
@echo off
rem =====================================================
rem deploy.bat - copies build output to the release share
rem Usage: deploy.bat <environment>
rem =====================================================
setlocal enabledelayedexpansion
set "ENV=%~1"
if "%ENV%"=="" (
echo Usage: deploy.bat ^<environment^>
exit /b 1
)
set "SRC=%~dp0build"
set "DEST=\\fileserver\releases\%ENV%"
if not exist "%SRC%" (
echo ERROR: build output not found at "%SRC%"
exit /b 1
)
robocopy "%SRC%" "%DEST%" /MIR /R:2 /W:5
if %ERRORLEVEL% GEQ 8 (
echo ERROR: robocopy failed with code %ERRORLEVEL%
exit /b 1
)
echo Deployment to %ENV% completed successfully.
endlocal
exit /b 0Scoping State and Checking Every Fallible Command
Begin every script with 'setlocal' so variable changes don't leak into the caller's environment or persist across repeated invocations in the same session — forgetting this is a common cause of a script that 'works the first time but not the second'. Treat robocopy specially: unlike most commands, exit codes 0-7 indicate success (files copied, or nothing to do), while 8 and above indicate a real failure, so a naive 'if errorlevel 1' check will incorrectly flag a normal robocopy run as failed; always compare with 'if %ERRORLEVEL% GEQ 8' for robocopy specifically.
Cricket analogy: A fresh new ball being used at the start of each innings rather than carrying over wear from the previous match mirrors setlocal resetting a script's variable scope on every invocation.
Always pair setlocal with endlocal (or let the script end naturally, which implicitly calls endlocal) so variable and PATH changes don't persist into the calling shell. If a script is meant to export a result to the caller, use 'endlocal & set "RESULT=%RESULT%"' — the special syntax that lets a single value survive the endlocal boundary.
robocopy's exit codes are bitmask flags, not a simple success/failure boolean — codes 1 through 7 all indicate some form of success (files copied, extra files, mismatched files) while only 8 or higher is a genuine failure. Using a plain 'if errorlevel 1' check after robocopy will falsely treat a perfectly successful copy as an error.
- Quote every path and variable expansion to prevent silent argument splitting on embedded spaces.
- Place the opening quote immediately before the variable name in set "VAR=value" to avoid trailing-space bugs.
- Start every script with @echo off, a header comment, and setlocal to scope state properly.
- Pair setlocal with endlocal, or rely on the script's natural end, to avoid leaking variables into the caller.
- Check %ERRORLEVEL% after every command that can fail, capturing it into a variable before it can be overwritten.
- Treat robocopy's exit codes specially — only GEQ 8 indicates real failure, not any nonzero value.
- Use exit /b rather than exit so only the current script exits, not the entire cmd.exe session.
Practice what you learned
1. Why is 'if exist "%FILE%"' preferred over 'if exist %FILE%'?
2. What robocopy exit code range indicates a genuine failure rather than a successful copy?
3. Why put the opening double quote immediately before the variable name in 'set "VAR=value"'?
4. What is the main purpose of calling setlocal at the top of a batch script?
5. What is the difference between 'exit' and 'exit /b' inside a batch script called from another script?
Was this page helpful?
You May Also Like
Common Batch Script Patterns
A catalog of reusable Windows batch scripting idioms — looping over files, parsing command output, handling arguments, and structuring error paths.
Debugging Batch Scripts
Practical techniques for tracing execution, inspecting variable state, and isolating failures in Windows batch files.
Batch Script Quick Reference
A condensed lookup of essential Windows batch syntax — variables, control flow, string operations, and exit codes — for day-to-day scripting.
Related Reading
Related Study Notes in Microsoft Technologies
Browse all study notesWindows 10 / UWP Development Study Notes
.NET · 30 topics
Microsoft TechnologiesMFC (Microsoft Foundation Classes) Study Notes
C++ · 30 topics
Microsoft TechnologiesSilverlight Study Notes
.NET · 30 topics
Microsoft TechnologiesXAML Study Notes
.NET · 30 topics
Microsoft TechnologiesWPF (Windows Presentation Foundation) Study Notes
.NET · 30 topics
Microsoft TechnologiesMVVM Design Pattern Study Notes
.NET · 30 topics