バッチファイル（）とは、MS-DOS、OS/2、Windowsでのコマンドラインインタプリタ（シェル）に実行させたいコマンド（指令または命令）列をテキストファイルに記述したもの。バッチファイルを実行すると、シェルプログラム（一般にCOMMAND.COMやcmd.exe）がそのファイルを読み、その中のコマンドを（通常）一行ずつ実行する。バッチファイルはUNIXおよびUnix系オペレーティングシステムのシェルスクリプトに相当する。主に複数の処理をまとめて記述しておき、繰り返し実行するバッチ処理のために用いられる。

DOSのバッチファイルの拡張子は.BATである。他の環境のバッチファイルの拡張子は様々である。例えば、Windows NTやOS/2では.CMDが使われており、というフリーウェアでは.BTMが使われている。

AUTOEXEC.BATはブート処理の中で実行される特別なバッチファイル（自動実行バッチファイル）である。

 歴史 
マイクロソフトのDOSとWindowsのバッチプログラミングはこれらのオペレーティングシステム (OS) 製品のリリースとともに発展してきた。これらのOSに付属するコマンドインタプリタは2つの動作モードを持つ。1つは対話モード（コマンドプロンプトに従ってユーザーがコマンドを入力すると即座に実行される）、もう1つはバッチモード（あらかじめ設定されたコマンド列を実行する）である。どちらのモードもUNIXのシェルや1980年代初期のCP/Mなどのコマンドインターフェイスがその考え方のベースとなっている。

MS-DOSオペレーティングシステムのバッチプログラム用インタプリタはCOMMAND.COMである。MS-DOS用バッチプログラムのうち比較的単純なコマンドはCOMMAND.COM自身が処理し（内部コマンド）、複雑なものは別の実行ファイルを呼び出して処理する（外部コマンド）。この系統のバッチプログラミングはMS-DOSからWindows 95、Windows 98、Windows Me（Windows 9x系）まで発展していった。

Windows 2000およびWindows XP以降はMS-DOSではなくWindows NTに基づいている。この系統（Windows NT系）ではcmd.exeというコマンドラインインタプリタ（コマンド行インタプリタ）が使われ、COMMAND.COMとはある程度の互換性がある。MS-DOSのいくつかの機能が削除されているが、MS-DOSやMS-DOSベースのWindowsにはない新たな機能も多く追加されている。互換性維持のためCOMMAND.COMも未だに搭載されている。なお、cmd.exeに対しては「コマンド プロンプト」という名前のショートカットが既定で作成されており、便宜上GUIに表示される名前としても各所で使用されているが、一般的な技術用語のコマンドプロンプトとは別物である。cmd.exeで利用可能なコマンドは「Windowsコマンド」とも呼ばれる。

2006年には.NET Frameworkを基盤に動作する、モダンなオブジェクト指向のシェル環境としてWindows PowerShellがリリースされた。Windows PowerShellではdirなどの従来のWindowsコマンドに対するエイリアスが用意されており、完全ではないがある程度の互換性がある。のちにPowerShellはWindows以外の環境にも.NET Core/.NETベースで移植され、クロスプラットフォームとなっている。

 代表的なコマンド 

以下ではWindows NT系のcmd.exeで利用できるWindowsコマンドの仕様に従って解説する。構文規則において、[と]で囲まれたものはオプション（省略可能）であることを意味する。詳しくは公式リファレンスを参照のこと。/?オプションを付けてコマンドを実行することで、そのコマンドのヘルプを表示することもできる。

BASICやPascalに代表される古いプログラミング言語や、MS-DOSおよびWindowsなどのファイルシステムではアルファベットの大文字・小文字を区別しない仕様となっている影響から、バッチファイルもほとんどの場面で大文字・小文字を区別しない。歴史的な経緯から、ヘルプのメッセージ内ではコマンド名やオプション名が大文字アルファベットで表記されているが、本記事では引用を除いて小文字アルファベットで記載するものとする。

 echo 
echoコマンドは標準出力にメッセージを表示する。メッセージ文字列をダブルクォーテーションやシングルクォーテーションなどで囲む必要はない。

また、echo onおよびecho offでコマンドエコーのON/OFFを切り替えることができる。コマンドエコーは既定でONになっており、各コマンド行を実行する前に標準出力にその内容を表示する。この動作は通常必要とされないので、echo off コマンドをバッチファイルの最初に記述してそれを防ぐ。しかし、そのままでは echo off コマンド自体は表示されてしまう。単価記号 @ をコマンド行の先頭に置くと、その行は表示されないようになっているので、多くのバッチファイルには @echo off という行が先頭にある。

 rem 
コメント行はremで始める。remもコマンドの一種である。コメントや注釈を意味するの略で、BASIC系言語のremステートメントと類似のものである。

 cls 
テキスト画面をクリア（消去）し、ウィンドウを既定の状態にする。

 color 
テキスト画面の背景色と前景色を変更する。16進数の0からfまで全16色のカラーコードが割り当てられており、引数として背景色と前景色を2桁の16進数で指定する。1桁の16進数を指定した場合、前景色として使われ、背景色は既定値となる。背景色と前景色に同じ値を指定した場合は失敗する。引数を指定しなかった場合、背景色と前景色が既定値にリセットされる。

 call 
引数で指定した別のバッチファイルや、引数で指定したラベルを持つプログラム（サブルーチン）を呼び出し、制御を移す。

 if 
多くの汎用プログラミング言語における条件分岐構文の1つとしてif文があり、バッチファイルでもコマンドとしてサポートされている。

基本構文:
if [not] ERRORLEVEL <number> <command> [else <expression>]
if [not] <string1>==<string2> <command> [else <expression>]
if [not] exist <filename> <command> [else <expression>]
拡張構文:
if [/i] <string1> <compareop> <string2> <command> [else <expression>]
if CMDEXTVERSION <number> <command> [else <expression>]
if defined <variable> <command> [else <expression>]
既定では条件が真の場合にサブコマンドを実行するが、notを指定すると条件が偽の場合にサブコマンドを実行する。

ERRORLEVELは直前のコマンドの実行結果（終了ステータス）を格納しているシステム環境変数の1つであり、通例0が正常終了を、それ以外が異常終了（エラーコード）を意味する。コマンドの基本構文では後続指定する<number>の値以上である場合に真となる。

existは後続指定するファイルが存在するかどうかをテストし、存在する場合は真となる。

 例 
カレントディレクトリに test.txt というファイルが存在するかどうかに応じて処理を分岐する。存在する場合は拡張子.txtが関連付けられているアプリケーションソフトウェアで開く。
if exist test.txt (
 echo test.txt が見つかりました。
 test.txt
) else (
 echo test.txt が見つかりません。
)

 for 
C言語を代表とする多くの汎用プログラミング言語はループ構文の1つとしてfor文をサポートするが、バッチファイルのコマンドはforeach文に相当する。もともとファイルやディレクトリ（フォルダー）やテキスト文字列の集合（）の中から各要素に対して指定コマンド（サブコマンド）を実行するものであるが、数値の範囲を指定することもできる。

基本構文:
for {%%|%}<variable> in (<set>) do <command> [<commandlineoptions>]
ここで、<variable>はコマンド専用の変数であり、アルファベット1文字のみが利用可能である。大文字と小文字は区別される。正規表現で書くならば、許可される文字は[A-Za-z]となる。コマンドをコマンドラインシェル上で直接実行する場合は%iのようにパーセント記号を1つだけ使う。バッチファイル内に記述する場合は%%iのようにパーセント記号を2つ使う。

<set>にはファイルのグループを1つ以上指定することもできる。ワイルドカード（*および?）を利用することもできる。以下は有効な集合の例である。
(*.txt)
(*.txt *.png *.docx)
(2024_01_??.log)

<commandlineoptions>はサブコマンド<command>に対するオプションである。

 コマンド拡張 
の直後にスペースを空けて、コマンド自体に対するいくつかの拡張オプションを指定することも可能である。

 ディレクトリのみ 
ファイルではなくディレクトリのみを対象とする。
for /d {%%|%}<variable> in (<set>) do <command> [<commandlineoptions>]

 再帰 
サブディレクトリも対象にする。
for /r [[<drive>:]<path>] {%%|%}<variable> in (<set>) do <command> [<commandlineoptions>]

 値の範囲で反復 
変数の値を開始値<start#>と終了値<end#>の間でステップ量<step#>だけ変化させながら、指定コマンドを反復実行する。
for /l {%%|%}<variable> in (<start#>,<step#>,<end#>) do <command> [<commandlineoptions>]
まず変数に開始値が代入され、指定コマンドが実行される。次に変数の値をステップ量だけ変化させ、再び指定コマンドを実行する。ステップは正の数だけでなく負の数も指定可能である。ステップが正のとき、変数の値が終了値より大きくなった場合は指定コマンドを実行せず終了する。ステップが負のとき、変数の値が終了値より小さくなった場合は指定コマンドを実行せず終了する。

 cd 
指定したディレクトリを「カレントディレクトリ」として設定する。便宜上の概念から、指定ディレクトリに「移動する」とも言われる。相対パスは、で設定したカレントディレクトリを基準に扱われるようになる。引数を指定しなかった場合、カレントディレクトリのフルパスを出力する。

 dir 
指定したディレクトリの中のファイルおよびサブディレクトリのリストを表示する。引数を指定しなかった場合はカレントディレクトリが対象となる。オプションを設定することで、条件に合致する特定のファイルのみ表示することもできる。

このコマンドとリダイレクトを使ってテキストファイル形式のファイルリストを作ることなどもできる。

 shift 
バッチファイルにおいて、バッチ引数の位置を変更する。

バッチファイルをコマンドラインシェルから実行する際は10個以上の引数を指定することもできるものの、バッチファイルからは%0 - %9の範囲の引数しか扱えない仕様となっている（%0はバッチファイルの名前を表すため、実質的には9個の引数が使用可能）。

コマンドによって、引数の値が1つ前の引数にコピーされる。つまり%1の値は%0にコピーされ、%2の値は%1にコピーされる。これにより、バッチファイルで疑似的に10個以上の引数を扱えるようになる。

オプションとして/nがあり、シフトを開始する引数の番号を指定できる。nには0から8までの番号を指定できる。例えばは%3を%2に、%4を%3にシフトするが、%0と%1は変更されない。

 例 
起動コマンド行:
test.bat ar1 ar2 ar3 ar4 ar5 ar6 ar7 ar8 ar9 ar10
バッチファイル test.bat の内容:
@echo off
echo 第 0 引数は %0 です。
echo 第 9 引数は %9 です。
shift
echo 引数の位置を変更しました。
echo 第 0 引数は %0 です。
echo 第 9 引数は %9 です。
出力:
第 0 引数は test.bat です。
第 9 引数は ar9 です。
引数の位置を変更しました。
第 0 引数は ar1 です。
第 9 引数は ar10 です。

 set 
「cmd.exe環境変数」を表示・設定・削除する。引数なしでコマンドを実行した場合、現在設定されている環境変数の一覧を表示する。

基本構文:
set [<variable>=[<string>]]
set [/p] <variable>=[<promptString>]
set /a <variable>=<expression>

cmd.exe環境変数は、システム環境変数やユーザー環境変数とは異なり、起動中のcmd.exeのインスタンスおよびそこから起動した子プロセスにのみ影響する一時的な環境変数である。

 動的な環境変数 
で表示されるヘルプには、動的な環境変数に関する注意事項が記載されている。

続いて列挙される動的な環境変数とその説明文（日本語版）の引用を以下に記載する。
+ 環境変数 説明文 備考 %CD% 現在のディレクトリ文字列に展開します。 カレントディレクトリを表す。コマンドなどで変更可能。 %DATE% DATE コマンドと同じフォーマットで現在の日付に展開します。 システム時刻に依存する。コマンドでも変更可能。既定の表示形式はシステム設定に依存する。 %TIME% TIME コマンドと同じフォーマットで現在の時刻に展開します。 システム時刻に依存する。コマンドでも変更可能。既定の表示形式はhh:mm:ss.msであるが、時間 (hour) 部分が1桁台の間は先頭がゼロではなく空白となる。 %RANDOM% 0 から 32767 の間の任意の 10 進数に展開します。 疑似乱数を生成する。 %ERRORLEVEL% 現在の ERRORLEVEL の値に展開します。 直前のコマンドが正常終了したか、または異常終了したかを整数値で表したもの。0が正常終了で、それ以外が異常終了である。通例コマンドと組み合わせて使う。 %CMDEXTVERSION% 現在のコマンド プロセッサ拡張機能のバージョン番号に展開します。 通例コマンドと組み合わせて使う。 %CMDCMDLINE% コマンド プロセッサを起動したオリジナル コマンド ラインに展開します。 - %HIGHESTNUMANODENUMBER% このコンピューター上の最大の NUMA ノード番号に展開します。 詳細はNUMAアーキテクチャのシステムサポートに関する情報を参照のこと。
これらの環境変数の値をコマンドで変更することはできない。新たに同名のcmd.exe環境変数が生成され、動作がオーバーライド（上書き）されてしまうだけである。

 setlocal/endlocal 
コマンドで作成する変数はcmd.exe環境変数であり、既定でグローバル変数のような性質を持つ。つまり、バッチファイル内のどこからでもその変数にアクセスすることができてしまい、またcmd.exeのインスタンスが生き残っている限り、バッチファイルの終了後もその変数が維持されてしまう。特定のバッチファイル内限定、あるいはサブルーチンのような一部の領域でのみ使用することが想定されている変数は、事前にコマンドを実行しておくことでローカル変数とすることができる。

また、コマンドを使用することで環境変更のローカル化を終了し、対となるコマンドが実行される前の環境変数の状態を復元することができる。

 環境変数の遅延展開 
以下のように環境変数をコマンドの中で書き換えるケースを考える。
set my_variable=100
echo my_variable is %my_variable%
if %my_variable%==100 (
 set /a my_variable+=1
 echo my_variable is %my_variable%
)
echo my_variable is %my_variable%

実行結果:
 my_variable is 100
 my_variable is 100
 my_variable is 101

my_variableの値はコマンドの実行後には確かに書き換わっていることが確認できるが、コマンドの実行中に（サブコマンドグループの実行中に）参照すると、直感に反して書き換え前の値が出力されてしまう。これは、環境変数の展開が、既定では各コマンド行の解析時に一度だけ実行されるモードになっているからである。

事前にコマンドを実行し、かつ%の代わりに!を使って環境変数を囲むことで、環境変数を実行時に（サブコマンドによって実際に参照されるタイミングで）遅延展開することが可能となる。
set my_variable=100
echo my_variable is %my_variable%
if %my_variable%==100 (
 set /a my_variable+=1
 @rem 環境変数の遅延展開を有効化する。
 setlocal enabledelayedexpansion
 echo my_variable is !my_variable!
 endlocal
)
echo my_variable is %my_variable%

実行結果:
 my_variable is 100
 my_variable is 101
 my_variable is 101

 拡張子による動作の違い 
Windows NT系では.cmdだけでなく従来の.batもバッチファイルの拡張子として使用可能だが、動作に微妙な差がある。

以下のコマンドを記述したバッチファイルがあるとする。実行すると、は成功して環境変数ERRORLEVELが0に設定され、は失敗して環境変数ERRORLEVELが1に設定され、set my_variable=100は成功する。
@echo off
setlocal
echo ErrorLevel is %ERRORLEVEL%
color 00
echo ErrorLevel is %ERRORLEVEL%
set my_variable=100
echo ErrorLevel is %ERRORLEVEL%
endlocal

拡張子を.cmdとした場合の実行結果:
ErrorLevel is 0
ErrorLevel is 1
ErrorLevel is 0

拡張子を.batとした場合の実行結果:
ErrorLevel is 0
ErrorLevel is 1
ErrorLevel is 1

最後の出力結果に違いが生じる原因は、.cmdではコマンドは成否にかかわらず常にERRORLEVELを変更し、成功した場合はERRORLEVELが0にリセットされるのに対し、.batではコマンドは失敗した場合にのみERRORLEVELを変更する動作となるからである。つまり、.cmdのほうがいくらか安全な動作をする。.batの動作は直感的ではなく、驚き最小の原則に反しているといえる。ただし、このルールに従わないコマンドもいくつかあり、例えばやは、成否およびバッチファイルの拡張子によらず常にERRORLEVELを変更しない。

 例 

 単純なバッチファイルの例：
@echo off
cls
echo.
echo Hello World, press any key to start AProgram.exe!
pause > nul
AProgram.exe
echo.
echo AProgram has finished whatever it was doing. Have fun today!画面出力:Hello World, press any key to start AProgram.exe!

AProgram has finished whatever it was doing. Have fun today!
 Hello world
@echo off
echo Hello world!!
pause > nul画面出力:Hello world!!
 Fizz Buzz
 3の倍数ならFizz、5の倍数ならBuzz、15の倍数ならFizz Buzzと表示し、FizzBuzz.txt に結果を出力
@echo off
setlocal enabledelayedexpansion
cd /d %~dp0
if exist %cd%\FizzBuzz.txt del %cd%\FizzBuzz.txt
for /l %%i in (1,1,100) do (
set echo=
set /a f=%%i%%3,b=%%i%%5
if !f!==0 set echo=!echo!Fizz
if !b!==0 set echo=!echo!Buzz
if "!echo!"=="" set echo=%%i
echo !echo!>>%cd%\FizzBuzz.txt
)画面出力:なし
 FFmpegでエンコード バッチファイルの置かれたディレクトリを終了するまで１０秒おきに監視して、tsファイルを発見次第バッチファイルと同じフォルダに置かれたffmpeg.exeでmp4にエンコードしたあと、元のファイルを削除する。 エンコードされたファイルが既に存在する場合は上書きする。echo off
cd %~dp
:loop
for %%i in (*.ts) do (
 call :encode %%i%
)
timeout 10
goto :loop

:encode
ffmpeg.exe -i "%1" -vf bwdif=1 -c:v libx264 -c:a copy -bsf:a aac_adtstoasc ^
-preset fast -aspect 16:9 -b:v 5500k -y "%~n1.mp4"
del %1
exit /bまず、forループでファイルが有るかどうかチェックする。見つかったファイルは:encodeというサブルーチンに渡される。これはエンコード後のファイル名をつける際の%~n1で元のファイルのファイル名だけを抽出する必要があるからだ。 encodeの中でファイルをffmpegに渡してエンコードし、その後delコマンドでファイルを削除したら、サブルーチンが終了し、メインルーチンに戻る。tsファイルがまだ残っていれば再びエンコードする。なければforループから抜け出し、10秒間待機する。再びtsファイルを発見すると順次エンコードする。

 互換コマンドインタプリタ 

マイクロソフト以外が提供するコマンドインタプリタにも様々なコマンドインタプリタがあり、強化された機能を提供している。 はその一例である。

バッチプログラムを実行ファイルに変換するコンパイラもマイクロソフト以外からいくつか登場しているが、その品質は様々である。

IBMのOS/2オペレーティングシステムもマイクロソフトのOSと似たようなテキストベースのコマンド機能を持っている。