バッチファイル界の魔境『遅延環境変数』に挑む(おまけもあるよ)

  • 12
    いいね
  • 2
    コメント
この記事は最終更新日から1年以上が経過しています。

 今回は、forと双璧を為すバッチファイル界の魔境、遅延環境変数に挑みたいと思います。

1.遅延環境変数とは?

 さて、大分前に投稿した.bat(バッチファイル)のforコマンド解説。の中で、次のようなことを述べました。(見なくても問題ないです)

 特に遅延環境変数については、for文を使う上でほぼ確実に理解する必要が出てくると思われます。

 その理由を説明するには、バッチファイルでの「変数を読み込むタイミング」について知っておく必要があります。

変数の読み込みタイミング

 次のコードをご覧下さい。

例1.bat
@set num=1
@if %num% == 1 (
    set /a num+=1
    echo %num%
)
@pause

 ちなみに、このページ内にあるコード表示は、そのままコピー&ペーストでバッチファイルとして動くようになっています。 実行しても、ええんやで。

 さて、話を戻して…。上のコード、実行結果はどうなると思いますか?
 まず最初はnumが1なので、if文は真になりますね。ここまでは特に大したことはありませんが…。
 if文の最初で、numに1を加算しています。つまり、numの値は2になっているはずです。次の行で出力しているので2と表示されるはずなのですが…?

 このコード、出力結果は1になります。

 この現象こそが、変数を読み込むタイミングが顕著に影響を及ぼす例です。
 通常変数は%記号で囲みますが、この%で囲った変数は、その変数がある行に『入った瞬間』にすべて展開されます。

 そして、カッコで囲った複数のコマンドは、その行数に関わらず、1行とみなして変数が展開されます。

 つまりこのコード、if文に入った瞬間に変数が全て展開されてしまうので、echo時に加算が反映されていないのです。

 加算自体は行われていますので、カッコを抜けた後に再度echoしなおせば、きちんと2と表示されます。


 というわけで、変数の読み込みタイミングは行に入った瞬間であること、カッコは1まとまりと認識されることが分かりました。

 では、遅延環境変数とは何か?についての答えを出したいと思います。

 遅延環境変数とは、変数を読み込むタイミングを遅らせる変数である!

2.遅延環境変数とは?

 遅延環境変数を使うには、変数を囲む%記号を!記号に変えるだけ。
 こうすると、その変数を読み込むタイミングが、『行に入った瞬間』から『その変数を使うコマンドにたどり着いた瞬間』になります。

 試しに、先ほどのコードのecho行を遅延環境変数に変えてみましょう。

例1.bat
@setlocal enabledelayedexpansion
@set num=1
@if %num% == 1 (
    set /a num+=1
    echo !num!
)
@pause

 これで、きちんと2がechoされます。ですが、変な行が増えていますね?

 setlocal enabledelayedexpansionと書かないと、遅延環境変数が使えませんので、使う場合は何も考えずに文頭に書いてください。

3.いつ使う?~①かっこ内で~

 遅延環境変数は、基本的にかっこの中で使うことが多いです。その理由は先述した通りですが、ではかっこを使うのってどんな時でしょうか。

 それは、if文とfor文です。もちろんほかのコマンドで使うこともありますが、群を抜いて最多はこの2つでしょう。それぞれどんな使い方をするのかについては、別に解説ページを設けていますので作者マイページからご覧いただければ幸いです。

4.いつ使う?~②変数を二重に読み込む~

 突然ですが。あなたはバッチファイルで数列の項を計算しています。n項めの数字を、number[n]という名前の変数にそれぞれ保存しました。

 さて、計算が終わり、いざまとめて出力する段になりました。forは難しいので、ラベルを使ってn回のループを行い、数列の各項の数字を出力したいです。

 ……といった場合。
 変数の名前はnumber[%n%]ですので、表示しようとしたらecho %number[%n%]%…?

 これでは動きませんね。なぜなら、%number[%n%]%という変数は、%number[%%]%という分割のされ方をしてしまうためです。ではどうすればいいか?

 !number[%n%]!としてみましょう。さながら数式の小かっこと中かっこのような役割を果たします。行に入った瞬間に%%コマンドに入った瞬間に!!が読み込まれるわけですね。

4-おまけ.変数を2重に読み込むその他の方法(2016/5/29追記)

 実は変数を2重に読み込む方法というのはほかにも存在します。それらをご紹介します。

方法1.call set~2回解釈させる~

 echo %number[%n%]%では動かないという話をしました。遅延を使わずに二重展開するにはどうすればよいでしょうか?
 答えは、echo文を2回解釈させるです。具体的には、call echo %%number[%n%]%%としましょう。

 callは、以降のコマンドを実行する、という効果を持ちます。(それ以外もたくさんありますが。)
 そしてコマンドを読み込むと、%は変数のしるしとして、%%%ひとつとして解釈されます。つまり、call echo %%number[%n%]%%echo %number[nの中身]%を実行しろ、という指示になり正常に実行できるというわけですね。

 ちなみに、!!としても同様の効果を得ることはできません。できるのは%%だけですので気を付けましょう。

 また、この使い方をすると、外側の変数(ここでは%num[nの中身]%のほう)は遅延読み込み扱いになります。callが終わった後にechoコマンドに辿り着くので、『その変数を使うコマンドにたどり着いた瞬間』という遅延の条件を強制的に満たさせるわけですね。
 ちなみに、内側(ここでは%n%のほう)は、↑の条件を満たさないため自動で遅延にはなりません。カッコ内でcallを用いた2重読み込みをしたい場合は、call echo %%number[!n!]%%とするとうまくいきます。曲芸みたいなことをしていますが、考えながら読めば理解は難しくないと思います。

方法2.set /a~計算式中の変数は常に遅延展開される~

 もうひとつ、変数の中身が数字の場合に限り可能な方法を挙げます。
それは、set /a a=number[%n%]とすることです。余分に変数が一つ必要になりますが、これで変数aの中に二重に展開された数が代入されます。数字を代入したい場合(カッコの中などで結構多いです)はこちらのほうがスマートだと思います。

 どういう原理かというと、まずset /a中では変数を%や!でくくる必要がありません。式中の変数は、すべてset /aコマンド開始時に自動で読み込まれます。
 コマンド開始時に……ということは、遅延展開ということですね。文中の%%は行に入った瞬間、!!もset /aより先に読み込まれますので、『くくった変数が読み込まれる→くくってない変数が遅延で読み込まれる』という流れを達成したことになります。!!も先に読み込んでくれますので、カッコ中ではset /a a=number[!n!]として問題ありません。

5.まとめ

 遅延環境変数は、①かっこ内で変数を使うとき、②変数を2重に読み込むときに使用します。とはいえ最初のうちは慣れないことと思いますので、様々なところでこまめにechoして挙動を理解しましょう。

 最後まで読んでいただき、有難うございました。
 誤り・感想・要望などありましたら、是非コメントにお願いいたします。

  • この記事は以下の記事からリンクされています
  • batノウハウからリンク