なたで日記

いろいろな思ったこと書きますヽ(^▽^ゞ)

batファイルでfor文内に変数を利用する場合の罠

time 2018/10/06

batファイルでfor文内に変数を利用する場合の罠

はじめに

今回は、batファイルを書いていてハマった問題を紹介したいと思います。

やりたかったことは、batファイルから外部のテキストファイルを読み込み、
そのテキストファイルの各行に記載されている文字を
別のバッチファイルへの引数に渡して処理をさせたいと思っていました。

しかし、なぜか別のバッチファイルの引数に文字列が渡らないという問題が発生。
原因を突き止めていきました。

ファイルをロードして処理するスクリプト

調査

再現するスクリプトを調べた結果、以下のことが分かりました。

まず別のテキストファイル(Text.txt)を用意します。

1
2
3
4

そして、この行ごとに処理を行うスクリプトを用意。

@echo off

for /f %%a in (./Text.txt) do (
	set line=%%a
	echo %line%
)

pause

上記の処理でechoにより、1,2,3,4を期待してたのですが、
これを実行すると、以下のようになります。

ECHO は <OFF> です。
ECHO は <OFF> です。
ECHO は <OFF> です。
ECHO は <OFF> です。
続行するには何かキーを押してください . . .

これはおかしいですね。

ECHO は <OFF> です。

この「ECHO は <OFF> です。」というのを調べた結果、
単純に変数自体の初期化ができていないときのエラーのよう。

もしかして、set line=%%a が実行されていない……?

ブロック文の環境変数の展開

さらに調査を続けると、
結論としては、遅延環境変数が問題であることが分かりました。

どうやら、Windowsのバッチファイルで、
括弧()をつかったブロック文を作った場合、
括弧に入った瞬間に内部の環境変数が展開されるようです。

従って、そもそもブロック内の echo %line% は、
その上の行の set line=%%a を実行される前に展開されてしまい、
%line%が未初期化のため、エラーになるのです。

修正方法

ブロック内で環境変数に代入しない

ブロック内で環境変数に代入し、代入結果を利用することが問題なので、
例えば、代入結果を利用しないように以下のようにするだけで修正できます。

@echo off

for /f %%a in (./Text.txt) do (
	echo %%a
)

pause

遅延環境変数を使用する

他の方法としては、
展開を遅らせる遅延環境変数を利用するというもの。

使用方法は、変数を使うときに%line%ではなく、
!line! のようにエクスクラメーションマークで囲います。

またこれを使用する前に、
setlocal EnableDelayedExpansionを実行して、
遅延環境変数が利用できるように設定しておく必要があります。

上記をふまえて以下のように書き換えましょう。

@echo off
setlocal EnableDelayedExpansion

for /f %%a in (./Text.txt) do (
	set line=%%a
	echo !line!
)

pause
endlocal

環境変数の展開タイミングによる他の問題例

今回、for文を使用したことで問題が発生しました。
ブロック文を使用する if文でも同様の問題が発生します。

@echo off

set num=1

if %num% equ 1 (
	echo %num%
	set num=2
	echo %num%
)

echo %num%

pause

「122」と出ることを期待して上記を実行すると

1
1
2
続行するには何かキーを押してください . . .

と出てしまいます。

これもブロック内を展開するタイミングで、
echo %num%が全て、echo 1 に展開されてしまうためです。

これもまた、遅延環境変数で解決できます。

@echo off
setlocal EnableDelayedExpansion

set num=1

if !num! equ 1 (
	echo !num!
	set num=2
	echo !num!
)

echo !num!

pause
endlocal

おわりに

面倒なので、普通の環境変数は
遅延環境変数と扱ってくれればいいのですけどもね。

とりあえずbatファイルなどのスクリプトは、
結構知らない仕様がまだまだあって怖い・・・。

最後まで読んでいただきありがとうございました。

コメントを残す




プロフィール

なたで

なたで

明るくて楽しいことが好きです!モノづくりや旅が趣味です!

RSS フィード RSS feed GitHub GitHub Twitter Twitter YouTube YouTube



sponsored link

タイムライン