バッチファイル

**バッチファイル**（）とは、主にDOS系およびWindows系オペレーティングシステムのシェルに実行させる複数のコマンド手順を、テキストファイルに記述したスクリプトファイルである。

コマンド列を上から順に解釈して処理を自動実行する仕組みとして、シェルスクリプトに相当する自動化記述の一形態に位置づけられる。利用者がファイル操作や設定変更などの定型作業を繰り返し実行する際に用いられる。

特徴として、逐次実行方式をとること、および環境に応じて`.BAT`や `.CMD` などの拡張子が使用されることが挙げられる。

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

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

Windows 2000およびWindows XP以降はDOSではなくWindows NTに基づいている。この系統（Windows NT系）では`[[cmd.exe]]`というコマンドラインインタプリタ（コマンド行インタプリタ）が使われ、`COMMAND.COM`とはある程度の互換性がある。DOSのいくつかの機能が削除されているが、DOSや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に代表される古いプログラミング言語や、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文があり、バッチファイルでもコマンドとしてサポートされている。

基本構文:

```dos
if [not] ERRORLEVEL <number> <command> [else <expression>]
if [not] <string1>==<string2> <command> [else <expression>]
if [not] exist <filename> <command> [else <expression>]
```

拡張構文:

```dos
if [/i] <string1> <compareop> <string2> <command> [else <expression>]
if CMDEXTVERSION <number> <command> [else <expression>]
if defined <variable> <command> [else <expression>]
```

既定では条件が真の場合にサブコマンドを実行するが、`not`を指定すると条件が偽の場合にサブコマンドを実行する。

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

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

#### 例
カレントディレクトリに test.txt というファイルが存在するかどうかに応じて処理を分岐する。存在する場合は拡張子`.txt`が関連付けられているアプリケーションソフトウェアで開く。

```dos
if exist test.txt (
  echo test.txt が見つかりました。
  test.txt
) else (
  echo test.txt が見つかりません。
)
```


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

基本構文:

```dos
for {%%|%}<variable> in (<set>) do <command> [<commandlineoptions>]
```

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

`&lt;set&gt;`にはファイルのグループを1つ以上指定することもできる。ワイルドカード（`*`および`?`）を利用することもできる。以下は有効な集合の例である。

```dos
(*.txt)
(*.txt *.png *.docx)
(2024_01_??.log)
```


`&lt;commandlineoptions&gt;`はサブコマンド`&lt;command&gt;`に対するオプションである。

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

##### ディレクトリのみ
ファイルではなくディレクトリのみを対象とする。

```dos
for /d {%%|%}<variable> in (<set>) do <command> [<commandlineoptions>]
```


##### 再帰
サブディレクトリも対象にする。

```dos
for /r [[<drive>:]<path>] {%%|%}<variable> in (<set>) do <command> [<commandlineoptions>]
```


##### 値の範囲で反復
変数の値を開始値`&lt;start#&gt;`と終了値`&lt;end#&gt;`の間でステップ量`&lt;step#&gt;`だけ変化させながら、指定コマンドを反復実行する。

```dos
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`は変更されない。

#### 例
起動コマンド行:

```dos
test.bat ar1 ar2 ar3 ar4 ar5 ar6 ar7 ar8 ar9 ar10
```

バッチファイル test.bat の内容:

```dos
@echo off
echo 第 0 引数は %0 です。
echo 第 9 引数は %9 です。
shift
echo 引数の位置を変更しました。
echo 第 0 引数は %0 です。
echo 第 9 引数は %9 です。
```

出力:

```text
第 0 引数は test.bat です。
第 9 引数は ar9 です。
引数の位置を変更しました。
第 0 引数は ar1 です。
第 9 引数は ar10 です。
```


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

基本構文:

```dos
set [<variable>=[<string>]]
set [/p] <variable>=[<promptString>]
set /a <variable>=<expression>
```


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

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

続いて列挙される動的な環境変数とその説明文（日本語版）の引用を以下に記載する。

これらの環境変数の値をコマンドで変更することはできない。新たに同名のcmd.exe環境変数が生成され、動作がオーバーライド（上書き）されてしまうだけである。

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

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

#### 環境変数の遅延展開
以下のように環境変数をコマンドの中で書き換えるケースを考える。

```dos
set my_variable=100
echo [1] my_variable is %my_variable%
if %my_variable%==100 (
  set /a my_variable+=1
  echo [2] my_variable is %my_variable%
)
echo [3] my_variable is %my_variable%
```


実行結果:

```text
[1] my_variable is 100
[2] my_variable is 100
[3] my_variable is 101
```


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

事前にコマンドを実行し、かつ`%`の代わりに`!`を使って環境変数を囲むことで、環境変数を実行時に（サブコマンドによって実際に参照されるタイミングで）遅延展開することが可能となる。

```dos
set my_variable=100
echo [1] my_variable is %my_variable%
if %my_variable%==100 (
  set /a my_variable+=1
  @rem 環境変数の遅延展開を有効化する。
  setlocal enabledelayedexpansion
  echo [2] my_variable is !my_variable!
  endlocal
)
echo [3] my_variable is %my_variable%
```


実行結果:

```text
[1] my_variable is 100
[2] my_variable is 101
[3] my_variable is 101
```


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

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

```dos
@echo off
setlocal
echo ErrorLevel is %ERRORLEVEL%
color 00
echo ErrorLevel is %ERRORLEVEL%
set my_variable=100
echo ErrorLevel is %ERRORLEVEL%
endlocal
```


拡張子を`.cmd`とした場合の実行結果:

```dos
ErrorLevel is 0
ErrorLevel is 1
ErrorLevel is 0
```


拡張子を`.bat`とした場合の実行結果:

```dos
ErrorLevel is 0
ErrorLevel is 1
ErrorLevel is 1
```


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

## 例

* 単純なバッチファイルの例：

```dosbatch
@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!
```
画面出力:
```dosbatch
Hello World, press any key to start AProgram.exe!

AProgram has finished whatever it was doing. Have fun today!
```

* Hello world

```dosbatch
@echo off
echo Hello world!!
pause > nul
```
画面出力:
```dosbatch
Hello world!!
```

* Fizz Buzz
: 3の倍数ならFizz、5の倍数ならBuzz、15の倍数ならFizz Buzzと表示し、FizzBuzz.txt に結果を出力

```dosbatch
@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にエンコードしたあと、元のファイルを削除する。  エンコードされたファイルが既に存在する場合は上書きする。
```dosbatch
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と似たようなテキストベースのコマンド機能を持っている。

## 脚注

### 注釈

### 出典

## 関連項目
* シェルスクリプト
* バッチ処理
* フィルタ (ソフトウェア)
* パイプ (コンピュータ)
* ジョブ管理システム
* Windows Script Host
* PowerShell