今回は、forと双璧を為すバッチファイル界の魔境、遅延環境変数に挑みたいと思います。
#1.遅延環境変数とは?
さて、大分前に投稿した.bat(バッチファイル)のforコマンド解説。の中で、次のようなことを述べました。(見なくても問題ないです)
特に遅延環境変数については、for文を使う上でほぼ確実に理解する必要が出てくると思われます。
その理由を説明するには、バッチファイルでの「変数を読み込むタイミング」について知っておく必要があります。
##変数の読み込みタイミング
次のコードをご覧下さい。
@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行を遅延環境変数に変えてみましょう。
@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して挙動を理解しましょう。
最後まで読んでいただき、有難うございました。
誤り・感想・要望などありましたら、是非コメントにお願いいたします。