プログラミング

Windows bat のFORやIFでは遅延環境変数で変数を扱う

-プログラミング

Windows bat のFORにおけるハマりポイント

WIndows batでバッチを作成してする際に良くハマるのが、FORやIFで変数が上手に展開できない問題です。

例えば、このようなバッチを作成した場合

@echo off

set num=0
for /l %%n in (1,1,10) do (
  set a=%%n回目
  echo %a%
)

頭の中ではこのように出力されると考えますが、

1回目
2回目
3回目
4回目
5回目
6回目
7回目
8回目
9回目
10回目

実際にはこのように変数に値が代入されることなくエラーが出力されます。

ECHO は <OFF> です。
ECHO は <OFF> です。
ECHO は <OFF> です。
ECHO は <OFF> です。
ECHO は <OFF> です。
ECHO は <OFF> です。
ECHO は <OFF> です。
ECHO は <OFF> です。
ECHO は <OFF> です。
ECHO は <OFF> です。

これは、仕様の問題で、括弧()をつかったブロック文を作った場合、

ブロックに入った瞬間に、ブロック内に何行あろうと1行と見なしてコードを読み込み内部の環境変数が全て展開されるためです。

つまりは、「set a=%%n回目」で変数を代入する前に「echo %a%」で変数が展開されてしまうためです。

これを解決するためには「遅延環境変数の展開」を利用します。

遅延環境変数の展開とは

遅延環境変数の展開をつかうと、変数を展開するタイミングをコードを読み込みでなく、変数を使う処理にたどり着いたタイミングとすることができます。

その結果、括弧内であっても変数に値を代入してから変数を展開することになるので、想定通りの変数を扱うことができます。

遅延環境変数の展開を使った対応方法

では、実際に遅延環境変数の展開を使ってbatを修正します。

ポイントは2つです。

ポイント

  1. setlocal EnableDelayedExpansionを追加
  2. 変数の展開は「%」ではなく「!」を使う

実際にコードを修正するとこのようになります。

@echo off
rem ポイント1
setlocal EnableDelayedExpansion

set num=0
for /l %%n in (1,1,10) do (
  set a=%%n回目
 rem ポイント2
  echo !a!
)
endlocal

実行結果はこのようになり変数が想定通り展開できるようになります。

1回目
2回目
3回目
4回目
5回目
6回目
7回目
8回目
9回目
10回目

-プログラミング