Windows Batch Scripting
Writing production-grade .bat/.cmd scripts for Windows automation.
Structure
@echo off
setlocal enabledelayedexpansion
REM === CONFIG ===
set "APP_NAME=MyTool"
set "LOG_FILE=%~dpn0.log"
REM === MAIN ===
call :check_admin
call :do_work
goto :eof
REM === FUNCTIONS ===
:check_admin
net session >nul 2>&1
if %errorlevel% neq 0 (
echo Requesting admin...
powershell -Command "Start-Process '%~f0' -Verb RunAs"
exit /b
)
goto :eof
:do_work
echo Working...
goto :eof
Key Patterns
Argument Parsing
if "%~1"=="" echo No args & exit /b 1
if /I "%~1"=="/?" call :help & exit /b 0
if /I "%~1"=="--help" call :help & exit /b 0
set "ACTION=%~1"
set "TARGET=%~2"
shift /1
Error Handling
call :some_func
if %errorlevel% neq 0 (
echo ERROR: Function failed with code %errorlevel%
exit /b %errorlevel%
)
For Loops
REM Files in directory
for %%f in (*.txt) do echo %%f
REM Line-based (command output)
for /f "tokens=*" %%l in ('dir /b *.txt') do echo %%l
REM Delimited parsing
for /f "tokens=1,2 delims=," %%a in (data.csv) do echo %%a = %%b
REM Range
for /l %%i in (1,1,10) do echo %%i
String Operations
set "var=hello world"
echo %var:~0,5% REM hello
echo %var:~6% REM world
echo %var:world=there% REM hello there
if /I "%var%"=="hello world" echo match
Delayed Expansion (important!)
setlocal enabledelayedexpansion
set "count=0"
for %%f in (*.txt) do (
set /a count+=1
echo !count!: %%f REM use !var! not %%var%% inside loops
)
File/Dir Checks
if exist "C:\path\file.txt" echo exists
if not exist "C:\path\" mkdir "C:\path"
if defined MYVAR echo MYVAR is set
Admin Elevation
net session >nul 2>&1
if %errorlevel% neq 0 (
echo Elevating...
powershell -Command "Start-Process '%~f0' -Verb RunAs"
exit /b
)
Common Functions
:log
echo [%date% %time%] %* >> "%LOG_FILE%"
goto :eof
:confirm
set /p ans="%* [y/N] "
if /I "%ans%"=="y" exit /b 0
exit /b 1
:die
echo FATAL: %*
pause
exit /b 1
Pitfalls
- %errorlevel% must be checked IMMEDIATELY after command -- gets overwritten
- Delayed expansion needed for !var! inside loops and if blocks
- Spaces in paths -- always quote: "C:\Program Files..."
%~dp0gives script directory with trailing backslashset /pleaves variable unchanged if user presses Enter (not empty)findstris the closest to grep -- usefindstr /i pattern file- PowerShell is better for anything beyond basic scripting -- consider switching