cmd.exe and batch files
cmd.exe and batch files
cmd.exe is the command processor on Windows NT-family systems. It debuted in 1993 with Windows NT 3.1 and ships with every Windows release. Since PowerShell arrived, cmd's role as an entry point for new code has shrunk, but we still meet it through legacy compatibility and small quick tasks.
1. Where cmd.exe sits
- MS-DOS era —
COMMAND.COMwas the 16-bit command processor. - From Windows NT 3.1 (1993) — the 32-bit
cmd.exebecame the standard command processor on the NT line. - It keeps a large share of DOS command compatibility, but adds new features (pipe handling, command extensions).
PowerShell is more capable, but cmd lives on for the following reasons:
- The lightest available shell. Usable immediately right after boot.
- A great many old automation scripts and installers still ship as
.bat. - Environment entry scripts like Visual Studio Developer Command Prompt are cmd-based.
2. .bat vs .cmd
.bat— the DOS-era extension. Compatibility first..cmd— introduced with Windows NT. Some built-in commands update errorlevel differently from.bat(append,dpath,ftype,set,path,assoc,prompt)..cmdis recommended for new files.
In practice the two are usually treated as the same thing.
3. Basic syntax
@echo off
setlocal enabledelayedexpansion
set NAME=world
echo hello, %NAME%
if "%NAME%"=="world" (
echo matched
) else (
echo no match
)
for %%f in (*.txt) do (
echo %%f
)
endlocal
Key elements:
@echo off— keeps subsequent commands from being echoed before they run.setlocal/endlocal— limits variable scope to the script.enabledelayedexpansion— enables!var!delayed expansion. Even when a variable is updated inside afor/ifblock, it reflects immediately.%var%— normal variable reference. Substituted once when the block is entered.!var!— delayed expansion. Re-evaluated each iteration.if errorlevel N— true when the last command's exit code is N or above. The exact code is%ERRORLEVEL%.
4. Variants of for
for %%f in (*.log) do echo %%f :: file list
for /R %%f in (*.md) do echo %%f :: recurse
for /L %%i in (1,1,5) do echo %%i :: 1..5
for /F "tokens=1,2" %%a in (file.txt) do echo %%a %%b :: parse file lines
Inside scripts use %%f, on the command line use a single percent %f.
5. Functions (label + call)
@echo off
call :greet alice
exit /b 0
:greet
echo hello, %~1
exit /b 0
exit /b N returns N to the caller. %~1 strips surrounding quotes from the first argument.
6. Code pages and Korean
cmd.exe defaults to cp949 on a Korean Windows install (Microsoft's extension to EUC-KR). Printing a UTF-8 file as is breaks Korean characters.
| Task | Command |
|---|---|
| Check current code page | chcp |
| Switch to UTF-8 (current session) | chcp 65001 |
| Switch to cp949 | chcp 949 |
chcp 65001 can break output on certain font/console-host combinations and in some external exes. It is relatively stable on Windows 11 + Windows Terminal.
The script file's own encoding is safest saved as cp949 (ANSI). When saving as UTF-8 the BOM presence can affect cmd's behavior.
7. bash equivalents
| Task | bash | cmd |
|---|---|---|
| Set variable | FOO=bar |
set FOO=bar |
| Read variable | $FOO |
%FOO% |
| Export env var | export FOO=bar |
set FOO=bar |
| Directory listing | ls |
dir |
| Current location | pwd |
cd (no arg) |
| Clear screen | clear |
cls |
| View file | cat file |
type file |
| Exit code | $? |
%ERRORLEVEL% |
| Conditional (success) | a && b |
a && b (cmd supports it) |
| Conditional (failure) | `a | |
| Comment | # ... |
REM ... or :: ... |
8. Limits
No object or module system. PowerShell or another language fits large-scale automation better.
Weak string handling and data structures. Arrays are simulated through index variables.
Unicode handling is awkward. Encoding mismatches between console output, file I/O, and external commands happen easily.
Debugging tooling is thin. Tracing usually means scattering echo calls.
9. Same task in cmd vs bash
@echo off
setlocal
if not exist build mkdir build
cd build
echo build directory ready
endlocal
#!/usr/bin/env bash
set -euo pipefail
mkdir -p build
cd build
echo "build directory ready"
10. Common pitfalls
set FOO = bar with spaces around = makes the variable name FOO (with trailing space) — keep it as set FOO=bar.
%var% not refreshing inside a for block — fix with setlocal enabledelayedexpansion + !var!.
Double-clicking a batch file closes the window the moment it finishes, hiding output — add pause at the end or run from cmd directly.
Korean text breaking — both code page and file encoding need to match.
if "%var%"=="value" becomes if ""=="value" when the variable is empty. The syntax still parses but the intent gets murky — keep the quotes around the variable consistent.
Closing thoughts
cmd and batch files sit in the legacy-compatibility seat. New automation lands more naturally in PowerShell or bash, with .bat reduced to an entry-point wrapper. Korean encoding and delayed expansion are the two spots where things go wrong most often.
Next
- cross-platform-scripts
- markdown
Microsoft Windows commands · Microsoft cmd · SS64 CMD · Microsoft chcp · Wikipedia cmd.exe for reference.