Windows コンソール プログラミング - プログラムの構文とか。
PROGRAM
プログラムの構文とか。
目次
- 実行環境について … スクリプトを実行するにはどうしたらいいか、そんなお話です。
- 文字を画面に表示してみる … まずは文字列を画面に表示するお話です。
- 実行時の通知の調整 … バッチファイル実行時の、コマンド行の表示を制御するお話です。
- 変数を使ってみる … コンソールではどうやって変数を利用するのかのお話です。
- 制御構文を使ってみる … IF 文など、バッチファイルでは馴染みが薄い気がする制御構文のお話です。
- 実行を一時とめてみる … 最後に自動的にコンソールが閉じるのを阻止する簡単な方法です。
実行環境について
まずは、どうやればプログラムを実行することが出来るのかについて触れてみようと思います。ここで想定しているのはバッチファイルです。バッチファイルというのは、コマンドプロンプトで実行したいコマンドをまとめて記載したファイルです。
作り方は非常に簡単で、メモ帳などのいわゆるテキストエディタを使って、コマンドプロンプトで実行したいものを行単位で書いていき、最後に拡張子を "BAT" として保存すれば完成です。
テキストエディタによっては、自動的に拡張子を "TXT" としてしまう場合があるので気をつけてください。
出来上がったバッチファイル ("*.bat") は、ダブルクリックすればそのまま実行することが出来ます。
実行するとコンソール (コマンドプロンプト) が表示されてそこへ実行内容が表示されるのですけど、あえて入力待ちをするなどして実行を途中で止めてあげないと、処理が終わったとたんにウィンドウが閉じてしまいます。
それがいやな場合は、あらかじめコンソールを立ち上げて、そこからバッチファイルを実行する必要があります。
やりかたとしては、まずコマンドプロンプトを起動させたら、"cd" コマンドをつかってバッチファイルの置いてあるフォルダまで移動します。そしてそこで、コマンドを入力するようにバッチファイル名を入力すると実行できます。
たとえば "C:\temp\test" というフォルダに、"test.bat" というバッチファイルが保存されていたとすると、次のような感じになります。 一応、フォルダを移動するところと、実際にバッチファイルを実行するところとに分けて書いてみますね。
c:
cd \temp\test
test.bat
バッチファイルの実行のところは、"test.bat" でなくて "test" というように拡張子を省いてもかまいませんけど、個人的には拡張子も書いておくのをお勧めします。
文字を画面に表示してみる
では、実際のプログラミングに移って見ましょう。といってもまずはそう言うには大げさな、画面に文字を表示する方法です。
文字を表示するには echo というコマンドを使用します。使い方はとっても簡単で、 echo に続けて表示したい文字をそのまま、とくに引用符などでくくることなく並べてあげれば大丈夫です。
さっそく、"Echo で文字を画面に表示してみる" という文字を表示するバッチファイルを作ってみましょう。
echo Echo で文字を画面に表示してみる
1行なのでコマンドプロンプトから直接実行しても確認できますけど、一応、"echo-test.bat" など、拡張子を BAT としたバッチファイルとして保存して実行してみると、バッチファイルを作成する感じが分かりやすいと思います。
また、改行だけを表示したい場合には、次のようにします。
echo.
さて、文字を表示するという観点で見ればこれでおしまいなのですけど、ついでなのでもう少し、ちょっと入り込んだお話をしようと思います。
バッチファイルでは、というかコマンドプロンプトでもそうですけど、"環境変数" というものを簡単に扱うことが出来るようになっています。環境変数というのは、パソコン起動時に設定される、名前と値がセットになったものです。Windows のインストールされているフォルダが分かるように "windir" という名前の環境変数が用意されていたり、他にも作業用のディレクトリを示す "TEMP" や "TMP" という名前の環境変数などたくさんあります。
バッチファイル中では、これらの環境変数の値を、簡単に取得する方法が用意されています。
その方法は非常に単純で、取得したい環境変数の名前の前後に "%" をつけてあげるだけで、その部分がその変数の値に置き換わってくれます。たとえば、上記でもさらっと紹介した、 "windir" という環境変数に保存されている値を、 echo を使って画面に表示してみましょう。
echo %windir%
これだけで完成です。これを実行してみると次のような感じで画面に文字が表示されます。Windows のバージョンやその他の環境によって違うので、まったく同じとは限りませんけど。
C:\WINNT
また、この方法は次のように、普通に混ぜて使うことができます。
echo システムディレクトリは%windir%です。
逆に、"%windir%" という文字そのものを表示したい場合には、それぞれの "%" を "%%" というように二重にします。
echo システムディレクトリは%%windir%%です。
このように "%%" とすることで、その文字は "%" であることを実行時に判断してもらうことが出来ます。環境変数のように "%" がついになる場合に限らず、1つだけでも重複させて記述します。
echo 今日の降水確率は 80%% です。
今回は echo のお話だったこともあり、また置き換わったかどうかを目で見るにはもってこいだったので、ここで取り上げてみました。けれどこの方法は echo のみならず、バッチファイル中のいろいろな場所で利用することが出来ますので、何かと使用する機会があると思います。
なお、環境変数に保存されている名前と値は、その意味こそ分かりませんけど、次の命令でとりあえず確認することが出来ます。
set
実行時の通知の調整
これまで echo によってバッチファイルの実験をしてきたのですけど、表示される文字列とあわせて "echo ......" といった命令そのものも画面に表示されていることに気がついたでしょうか。
他の命令ならば、何を実行しているのか知る上で表示されてもいい、または表示されるのが望ましい場合もあるでしょうけど、 echo に限っては結果だけ表示されれば、わざわざ echo 行まで表示される必要はないことと思います。第一、命令中の表示用文字列と、実際に表示される文字列とがダブってしまってみっともないですよね。
バッチファイルでは、実行する命令行を画面に表示されない仕組みが用意されています。
そのうちのひとつが、命令の前に "@" をつける方法です。echo に限らず、命令行の頭を "@" ではじめると、その行を実行する際に、その行自体が画面に表示されなくなります。もちろん、結果を画面へ返す命令ならばその結果はちゃんと表示されるので、 echo のような命令にはもってこいです。
さっそく、次のようなバッチファイルを用意して、違いを比べてみましょう。
echo @ なしの ECHO 行です。
@echo @ ありの ECHO 行です。
これだけ知っていれば、一応、表示したくない命令行を回避することができます。けれど、バッチファイル中のほとんどすべての命令を表示したくないときには、なかなか厄介です。そういうときは、先ほどまでと同じ echo 命令をつかって、命令行自体を表示するかどうかを指定することが出来ます。
指定の仕方は簡単で、 echo 命令に続いて、以降すべての命令を表示したい場合は "on" を、表示したくない場合は "off" を指定します。
いままでは純粋に、渡した文字列を表示してもらっていた echo が、別の役割で使われるため、なんだか理解しにくいかもしれません。でも、いっかい書いてみて、改めて眺めてみると意味的にもなんとなく理解できると思います。
@echo off
echo ECHO OFF での表示です。
このような感じのバッチファイルを作って、最初の @echo の行を on にしたり off にしたりして実行結果の違いを比べて見ましょう。先頭行の echo が @echo になっているのは、"echo off" が実行される際にそれが画面に表示されないようにするためです。
なお、 echo off の状態だと、 echo も @echo も命令行自体は表示されなくなります。ふたたび区別をしたい場合は echo on を実行してください。
ところで、 "on" や "off" を表示したい場合は…、どうしたらいいのでしょう …。調べてみてもそれらしいものが見つからなかったので、少し勘でいろいろと実験してみたところ…。
echo.on
このようにするとうまく行きました。改行のみを表示するときとの応用 (?) ですね。
"on" という文字列に限らず、任意の文字をつなげて書くことができるようです。ただし、たとえば "echo.on" というファイルがたまたまあったりするとそれを開こうとしてしまったり、そもそも正しい使い方かどうかわからないので気をつけてください。
変数を使ってみる
バッチファイルで使用する変数は、一般に 「環境変数」 といいます。この変数は基本的に文字列を保存するために使用されます。
バッチファイル内で環境変数を操作した場合、その値はそのシェル内、すなわち現在開いているウィンドウ、そのバッチファイル内、またはそこから CALL で呼び出したバッチファイル内でのみ有効です。CALL で呼び出した先で設定された環境変数も参照することが可能でした。
何はともあれ、どうやって利用するかを見てみましょう。
set 変数名=値
環境変数に値を代入するには SET という命令を使用します。ここで値に指定したものは文字列として解釈されます。
このように、SET に続けて 変数名=値 というように指定すると、その変数名に値が保存されます。変数名は大文字と小文字は同じ文字としてみなされます。値の両側を引用符でくくった場合、その引用符自体も値としてみなされます。
注意点は、まず、変数名と "=" との間には空白を入れてはいけないようです。保存できる値は最大 8kB で、全環境変数の容量は変数名と "=" も含めて 64MB に抑える必要があるとのことです。
値として指定できる文字は "<", ">", "|", "&", "^" を除いたすべてが利用でき、またこれらの文字を利用する場合は、その記号の前に "^" をひとつ加えてあげれば大丈夫…、らしいのですけど、実験した感じではうまくいきませんでした。値を引用符でくくった場合はそのままで大丈夫でした。
この値はバッチファイルのほとんどの場面で利用することが可能で、使用する場合は "%" を両方につけて %DATA1% というような感じにします。
たとえば DATA1 の値を画面に表示したい場合は次のようにします。
echo %DATA1%
このとき DATA1 に test という文字列が保存されていたとすると、次の行と同様に解釈され、画面に test という文字列が表示されます。
echo test
また、次のような使い方も出来ます。
set DATA1=echo
%DATA1% テスト
たとえばこのような内容だったとします。
1行目で DATA1 という変数に echo という文字列を設定しています。そして2行目の処理に移るわけなのですけど、このとき DATA1 の値が展開されて、最終的には次のように解釈されます。
echo テスト
この実行結果はもうおなじみですね。画面に、テスト という文字列が表示されるのでした。このように変数の値をそのまま命令に出来てしまったりします。
さて、変数の値を操作するお話です。環境変数は次のようにして値を連結することが出来ます。
set DATA1=最初の値
set DATA1=新しい値と%DATA1%
たとえばこのようにすると、最初の行で DATA1 という変数に "最初の値" という文字列が保存されます。続いて次の行ですけど、ここでも DATA1 という変数に値を保存しています。ただ、保存する値に %DATA1% が入っていることが、今回のポイントになります。
実行時には %DATA1% のところがその時点での DATA1 環境変数の値に置き換えられますので、実際には次の行として解釈されます。
set DATA1=新しい値と最初の値
つまり、最初の値が新しい値に追加されているのが分かるでしょうか。もちろん同じ変数名ではなく DATA2 でもなんでもいいので、このような感じにして複数の変数を連結することが可能となります。
不要となった変数を消去したい場合は、変数の値として何も指定しないで SET を実行します。
set DATA1=
これで DATA1 という変数が削除されることとなります。
これによって空文字が設定されるわけではなく削除となりますので、たとえば IF 制御構文をつかって if defined DATA1 を評価すると偽と判断されます。
SET では、数値の演算も出来るようです。古い資料だとその辺りが載っていなかったりするので、もしかすると比較的新しいバージョンからかもしれませんが。
set /a "式"
書式は上のような感じになります。
式の書き方は "変数名 = 式" という感じになります。変数名の両側に % をつけてはいけません。変数名は左辺のみならず、式の中にも普通に記述することが出来ます。必ずしも式を引用符で囲う必要はないのですけど、引用符の中でないと別の意味になる記号もわずかにありますので、個人的にはつけておくのが安心だと思います。
変数の値は数値として評価され、数字ではない文字が混じっていた場合は 0 とみなされる…、のですが、数字から始まる値である場合は数字以外の文字が出てくるところまでを有効な数字として評価するようです。たとえば "10a" だった場合は 10 として計算されました。
実際にどのような使い方になるか、簡単な例を挙げておきます。
set /a "DATA1 = (1 + DATA2) * 6"
このような感じで使用します。この例では、計算式 (右辺) の中では DATA2 という名前の変数の値が使われています。また計算した結果は DATA1 という変数へ保存されます。
演算子は C++ 言語と似たものが利用できるようになっていました。
* | 掛け算 | / | 割り算 | % | 商 (割り算の整数部分) |
---|---|---|---|---|---|
+ | 足し算 | - | 引き算 | ||
<< | 左シフト | >> | 右シフト | ||
& | ビット単位の論理積 | | | ビット単位の論理和 | ^ | ビット単位の排他的論理和 |
このほかに、"変数=式" の "=" の部分を次のものにすることで、たとえば "変数+=式" ならば、現在の変数の値に式によって得られた値を加えるといった意味を持たせることが出来ます。
*= | 掛け算 | /= | 割り算 | %= | 商 |
---|---|---|---|---|---|
+= | 足し算 | -= | 引き算 | ||
<<= | 左シフト | >>= | 右シフト | ||
&= | ビット単位の論理積 | |= | ビット単位の論理和 | ^= | ビット単位の排他的論理和 |
変数に対して、ユーザからの入力を受け付けることも出来ます。
set /p 変数名=表示文字列
このようにすると、画面に "表示文字列" が表示されて入力待ちになります。そして入力された文字列は、"変数名" で指定した変数へ保存されます。
制御構文を使ってみる
通常、バッチファイルは上の行から下へと順番に処理を行っていきます。けれど少し複雑なことをしようとすると、何行かを無視してみたり、または前へ戻ってみたりしたくなることがあります。それを可能にするのが制御構文です。
GOTO
一番単純な制御構文として GOTO が挙げられると思います。これを使用することで、任意の行へ処理を移動することが出来ます。使い方としては、あらかじめ "ラベル" 行を用意しておいて、そこへ移動したくなった段階で GOTO にラベルを引数として渡します。
他の制御構文を組み合わせないとあまり意味がないのですけど、感覚を覚えるためにも使い方を見てみましょう。
@echo off
echo GOTO の直前
goto LABEL
echo GOTO 直後、ラベル直前
:LABEL
echo ラベル直後
ラベルの書き方は、上記のようにラベル名の前にコロン ( : ) をつけた行を用意すれば大丈夫です。ここでは "LABEL" という名前のラベルを用意しています。そして少し上で、"goto LABEL" として、ラベル行までジャンプするように指定しています。ですので、実行処理としては、"goto LABEL" と ":LABEL" の間にある echo 行は実行されないことになります。
実際に実行結果をみてみると、次のようになるかと思います。
GOTO 直前
ラベル直後
間の "GOTO 直後、ラベル直前" というも字が表示される echo が無視されています。
IF
条件によって実行する命令を変えたいときに使用するのが、 IF 文です。
書き方としては次のような感じになります。基本的にはどれも似たような感じなのですけど、用途によっていろいろと書き方が変わる可能性があるので、思い当たるものをたくさん書いてみます。
rem 簡単なタイプ
if 条件 命令
rem 条件を満たさなかった場合の処理も指定する場合
if 条件 (命令) else (命令)
rem 複数行に分けて記述したい場合
if 条件 (
命令
) else (
命令
)
どれも少しだけ書き方が変わっているものの、本質は同じものです。
注意点としては、複数行に分けて書いている IF 文以外では、基本的にはひとつの命令しか実行できないことに注意してください。条件に合わせて複数の命令を実行したい場合は必ず、複数行に分けた方法で書く必要があります。
そして、 ( ) でくくられた中の1行ごとに、実行したい命令を記載すれば、条件を満たした際にはそれらが実行されるようになります。
次は、指定できる条件について見て行きましょう。
ERRORLEVEL 数値 | 直前に実行した命令が、指定した数値以上の戻り値を返した場合に条件成立とみなします。エラーレベルは通常、0 ならば正常終了、1 ならば異常終了を示すのが一般的だそうです。 |
---|---|
文字列 == 文字列 | 両方の文字列が一致した場合に条件成立とみなします。引数と比較する場合には "%1" == "test" のような感じに使用するらしいですが…、詳細は少し下で述べます。 |
EXIST パス | 指定されたパス (ファイルやフォルダ) が存在する場合に条件成立とみなします。使用する際に少しだけ気をつけておくことがあるので、それも下で述べます。 |
これらの条件は、条件式の頭に NOT をつけることで 「その条件が成立した場合には、不成立とみなす」 ようにすることができます。
また、このほかにも通常は次のものも利用できるそうです。
値 EQU 値 | 両辺に指定された値を比較して、それらが一致した場合に条件成立とみなします。値が数字文字だけで構成されている場合は、数値として比較されます。 |
---|---|
値 NEQ 値 | 両辺に指定された値を比較して、それらが一致しなかった場合に条件成立とみなします。値が数字文字だけで構成されている場合は、数値として比較されます。 |
値 LSS 値 | 両辺に指定された値を比較して、左辺の方が右辺よりも小さい場合に条件成立とみなします。値が数字文字だけで構成されている場合は、数値として比較されます。 |
値 LEQ 値 | 両辺に指定された値を比較して、左辺が右辺以下であった場合に条件成立とみなします。値が数字文字だけで構成されている場合は、数値として比較されます。 |
値 GTR 値 | 両辺に指定された値を比較して、左辺の方が右辺よりも大きい場合に条件成立とみなします。値が数字文字だけで構成されている場合は、数値として比較されます。 |
値 GEQ 値 | 両辺に指定された値を比較して、左辺が右辺以上であった場合に条件成立とみなします。値が数字文字だけで構成されている場合は、数値として比較されます。 |
CMDEXTVERSION 番号 | 実行環境 (コンソール) の内部バージョン番号が、指定された番号以上であった場合に条件成立とみなします。 |
DEFINED 変数名 | 指定された名前の変数が存在していた場合に条件成立とみなします。 |
EQU/NEQ/LSS/LEQ/GTR/GEQ については、その頭に /i をつけることで、大文字と小文字を同じものとして比較することが出来ます。
さて、上で挙げた条件式について、いくつか注意したいところがあるのでそれを紹介しておきます。
■ 文字列 == 文字列
文字列を比較する条件文ですが、基本的には、「文字列を引用符でくくる必要はない」 ということになっています。
if %1 == test (echo OK.) else (echo NG.)
このような感じで使用することが出来ます。
これは引数に渡された文字列が "test" であるかを調べているのですけど、たとえば引数に test という文字列を渡した場合、%1 が置き換えられて実行時には次のように解釈されます。
if test == test (echo OK.) else (echo NG.)
すると両辺が一致するため、条件成立とみなされ、 "echo OK." の方が実行されるという感じになりました。
ただし、もし引数を指定しないで実行した場合はどうなるでしょう。その場合、%1 が空文字に置き換えられて次のように解釈されてしまいます。
if == test (echo OK.) else (echo NG.)
この == という条件式は、左辺 (左側の文字列) と右辺とが指定されている必要があるのですけど、こうなると事実上、左辺が指定されていないということになってしまいます。
そこで通常は引用符をつけて、このような事態を回避するようにするのだそうです…。
if "%1" == "test" (echo OK.) else (echo NG.)
これは、引用符をつけたからといって決してそれが文字列だと宣言しているわけではないようです。こうすることで、たとえば引数が渡されなかった場合、今度は次のように解釈されるようになります。
if "" == "test" (echo OK.) else (echo NG.)
なおこれは、あくまでも元の文字列の両辺を引用符でくくった文字列、それを比較の対象として利用しているだけのようです。たとえば次のようにバッチファイルを若干調整してどう動くか実験してみましょう。
if %1 == "test" (echo OK.) else (echo NG.)
このように、引数に与えられたほうは引用符をつけないようにしてみます。
この上で、引数に test というものを渡すと、test == "test" が判定され、条件は不成立となります。さらに、"test" と引用符付きで引数に渡すと、"test" == "test" となり、無事に条件が成立ということになるのでした。
ところが、引用符はなくても動作する、また、見た感じは引用符はただも文字列の一部とみなしてよさそう、とおもいきや…。空白を含んだ文字列を比較しようとしたときには、引用符が必須になるのでした。
引用符がないと、空白をはさんでその先を命令とみなしてしまうため、構文エラーが発生します。
この微妙な仕様を回避するにはどうしたらいいのか…。それを解決できそうな方法として、引用符付きの引数を引用符なしに変換する、$~1 というものを利用するといいかもしれません。
これは空白を含んだ引数を受け取ったときに付加される引用符を取り除いたりする時に便利みたいなのですけど、これをつかえば、引用符がつかないものとしてデータを取り扱うことができるようになります。
もっとも、比較文字に空白を含む場合や引数が与えられなかったときに空文字になる場合も想定して、次のような条件式にするのが妥当だと思われます。
if "%~1" == "test" (echo OK.) else (echo NG.)
こうすることで、引数が test であろうと "test" であろうと、無事、条件が成立したとみなされるようになりました。ただこれでも完璧ということはなくて、引数の中に (たとえば te"st のように) 引用符があったりすると構文エラーとなってしまったりします。
これはどう回避したら良いのでしょうね…。ともかくこれは普通に "%1" で指定した場合でも同じ問題を抱えてしまうことになりますので、この "%~1" が今のところ一番お勧めな方法なのでした。
他にも、"%" 記号入りの文字列を比較したいような場合も注意が必要です。たとえば、1番目の引数に %2 という文字列が指定されたかどうかを判別するバッチファイルを作成したとしましょう。
if "%~1" == "%2" (echo OK.) else (echo NG.)
仮にこのようなことをやってしまうと、実行時にたとえ1番目の引数に %2 という文字列を渡したときに、%~1 の部分はちゃんと %2 という文字列に置き換わるものの、%2 の部分までも、こちらは2番目の引数で置き換えられてしまいます。
2番目の引数が指定されていないわけですからここは空文字になり、最終的に次のように解釈されてしまいます。
if "%2" == "" (echo OK.) else (echo NG.)
これでは一致せずに条件不成立となってしまいます。
これを避けるためにするには、右辺の %2 を置き換えられてしまわないようにするには、%2 だったところを %%2 というように指定する必要があります。
if "%~1" == "%%2" (echo OK.) else (echo NG.)
echo の実験のときのように、% を % そのものとして扱いたいときには %% と指定するやり方ですね。こうすることで、受け取った引数も、そして右辺に指定された文字列も、両方とも "%2" と判断されるため、条件成立となります。
■ EXIST パス
EXIST は、パスに指定されたファイルまたはフォルダが存在するかどうかを調べることができます。利用方法自体は非常に簡単で、たとえば C:\temp\test.bat というファイルがあるかどうかを調べたい場合には次のようにします。
if EXIST C:\temp\test.bat (echo OK.) else (echo NG.)
これで実際にファイルが存在していれば、条件成立となります。
フォルダについても同様にして調べることが出来るのですけど、この場合は 3 通りほどの調べ方が存在します。
1つ目はファイルのときとまったく同じやりかたです。まったく同じなので、ファイルであるのかフォルダであるのかの判断は出来ずに、単純にその名前のファイルまたはフォルダが存在していることしか分からないので注意が必要です。
rem ファイルと同じように調べる。
if EXIST C:\temp (echo OK.) else (echo NG.)
rem フォルダであることを強調する。
if EXIST C:\temp\ (echo OK.) else (echo NG.)
rem フォルダ内に必ず存在するとされるファイルを調べる(推奨)
if EXIST C:\temp\nul (echo OK.) else (echo NG.)
2番目は、フォルダの階層を示す記号 "\" をあえて最後にも持ってきて、それがフォルダを示すパスだけで終わっていることを明示するやり方です。正式な方法かは分からなかったのですけど、とりあえずこの方法でも出来たので書いてみました。
そして3番目が、「フォルダが存在するか」 を調べる本来の方法のようです。
やっていることは、システムの性質上、フォルダ内に必ず存在しているとされる NUL という名前のファイルが存在しているかどうかを調べています。フォルダならば必ずこのファイルが存在することとなっているため、これが見つかればそれはフォルダということになります。
逆に見つからなければそれはフォルダではないか、そもそも存在しないということになります。
FOR
リストアップされた文字列それぞれなどで、同じ処理を繰り返し実行したい場合に使用する制御構文です。複数行で書く場合のものは、命令自体も複数個をそれぞれの行に追加して記述することで、それらをまとめて繰り返し実行することが出来ます。
rem 一行で書く場合
FOR %%変数名 IN (文字列リスト) DO 命令
rem 複数行で書く場合
FOR %%変数名 IN (文字列リスト) DO (
命令
)
バッチファイル中では置き換えを回避するために %%ITEM というように "%" 記号をダブらせて指定します。ただし、コンソールから直接入力する場合は %ITEM とすればいいそうです。
ただし環境変数や引数を参照する場合は %%WINDIR%% のようにしてしまうと、おき返されないので混同しないように注意です。DO の後の命令の部分でも同じように、FOR で指定した引数を参照する場合は %%ITEM というようにします。
また、変数名は大文字と小文字は違うものとして扱うので注意してください。
では、簡単な例を見てみましょう。
for %%I in (test_a test_b test_c) do (echo %%I)
これは、文字列セットとして test_a, test_b, test_c の 3 つを直接指定しています。実行されるとこれら文字セット一つ一つに対して DO 以下のコマンドが実行されます。
すなわち、文字セットのそれぞれが echo の引数として渡され、画面に表示されることになります。実行結果は次の通りです。
test_a
test_b
test_c
引用符で文字セットの中の文字列をくくれば、空白も含めてそれはひとつの文字列として取り扱われます。
for %%I in (test_a "test_b test_c") do (echo %%I)
このように2つ目を "test_b test_c" とした場合、実行結果は次のようになります。
test_a
"test_b test_c"
このように、くくった引用符も一緒に変数に保存されてしまいます。
これが何かと厄介になることがあります。FOR の変数に対してもパス演算子を使用することができるようなので、%%~I として引用符を取り除いてあげるといいかもしれません。
for %%I in (test_a "test_b test_c") do (echo %%~I)
このようにしてあげると、くくってあった引用符がとれた結果が得られます。
test_a
test_b test_c
上記のバッチファイルを少し変更して、与えられた引数それぞれを画面に表示するものにしてみましょう。
for %%I in (%*) do (echo %%~I)
このように、与えられた引数すべてを一括して取得する %* という変数を文字セットとして指定してあげれば完成です。引数は空白でそれぞれが区切られた文字列なので、そのままで文字セットとして利用できます。
また FOR では、指定した拡張子のファイルをすべて取得してそのファイル名を表示することも出来ます。
それにはワイルドカードというものを利用するのですけど、たとえば *.txt とすると、現在いるフォルダ内から拡張子として .TXT を持つファイル、という意味になります。フォルダも指定したければ、C:\temp\*.txt といった感じです。
これをつかって、ディレクトリ内にある .BAT を拡張子にもつファイルを探してみましょう。
for %%I in (*.bat) do (echo %%~I)
たったこれだけです。
これを応用すれば、すべてのテキストファイルをひとつのファイルにまとめてみたり、フォルダ階層を表示したりなど、いろいろ出来そうですね。
演算子と処理記号
上記で幾度か、1行で書くタイプの書式では1つしかコマンドを指定できないと書きましたが、演算子や処理記号を利用することで複数の命令をひとつにまとめることが可能です。そうすることで、複数の命令を実行することも可能となります。
演算子と処理記号には次のものがあります。
rem 1行に複数の命令をまとめて書くことができます。
(命令1) & (命令2)
rem 左の命令が正常に終了した場合にのみ、右側の命令が実行されます。
(命令1) && (命令2)
rem 左の命令が正常に終了しなかった場合にのみ、右側の命令が実行されます。
(命令1) || (命令2)
rem 外側を括弧でくくることで、それらをまとめてひとつの命令としてまとめることが出来ます。
((命令1) & (命令2))
それぞれの命令そのものは括弧でくくる必要はないのですけど、予期せぬ自体をなるべく回避するためにも、上記のように単一の命令も括弧でくくっておくと安心だと思います。ちゃんと動くことが分かるならいいのですけど。
CALL
CALL というのは、途中で別のバッチファイルを実行するものです。構文は次のように、CALL の後に続けてコマンドプロンプトからバッチファイルを実行するのと似た感じで記載します。
call バッチファイル名 引数群
バッチファイルの途中で CALL が呼び出されると、そこに指定されているバッチファイルが実行された後、ふたたび呼び出しを行った CALL の次の行へ処理が戻ってきます。
実際に、次の2つのバッチファイルを作成して実験してみましょう。
■ test-call.bat
@echo off
echo %0: CALL で別のバッチファイルを呼び出します。
call test-call-2.bat
echo %0: CALL から戻りました。
■ test-call-2.bat
echo %0: CALL で呼び出されました。
これを実行してみると次のような結果が得られました。
test-call.bat: CALL で別のバッチファイルを呼び出します。
test-call-2.bat: CALL で呼び出されました。
test-call.bat: CALL から戻りました。
しっかりと、間にもうひとつのバッチファイルが出力した結果が表示されていることが分かります。
興味深かったのが、もうひとつのバッチファイルでは @echo off を指定しなかったのですけど、呼び出しもとのバッチファイルで指定した @echo off が有効でした。なお、もうひとつのほうであえて @echo on にしてみると、戻ってきたときには echo on の状態になっていました。
ところでそれぞれが独立していては、あまり CALL を利用する意味がありません。
呼び出し時には普通に呼び出すように任意の引数を指定することができますけど、ではその呼び出した先で得た情報はどうやって呼び出し元が取得できるのでしょう。
まず、呼び出し元で設定した変数が、引数を介さなくても呼び出し先で利用できるかを調べてみます。
■ test-call.bat
@echo off
set TEST=test
call test-call-2.bat
■ test-call-2.bat
echo %0: %TEST%
こうしてみると "test-call-2.bat: test" と表示され、しっかりと呼び出し元で設定した %TEMP% が参照できていることが確認できました。では、呼び出し先でまた、%TEST% の値を書き換えてみるとどうなるでしょう。
■ test-call.bat
@echo off
set TEST=test
call test-call-2.bat
echo %0: %TEST%
■ test-call-2.bat
echo %0: %TEST%
set TEST=書き換えました。
これを実行すると、呼び出して戻ってきた状態の %TEST% はしっかりと変更された値として取り出すことが出来ました。
これなら、なんだか引数をわざわざ指定する必要もなさそうですね…。でも、もし次のようにしたら引数が交換されるのかな…、ということで実験です。
■ test-call.bat
@echo off
call test-call-2.bat %2 %1
echo %0: %%1 = %1
echo %0: %%2 = %2
■ test-call-2.bat
echo %0: %%1 = %1
echo %0: %%2 = %2
これを実行してみます。なお、呼び出し時には、test_a と test_b の2つをこの順番で引数として渡してあります。
test-call-2.bat: %1 = test_b
test-call-2.bat: %2 = test_a
test-call.bat: %1 = test_a
test-call.bat: %2 = test_b
結果はこのようになりました。
呼び出し先のほうでは、呼び出される際に %2 %1 と、逆順に引数を渡しているので、当然のように逆順に値が表示されています。そして、呼び出し元へ戻ってきた後の表示では、引数に与えた順番どおりという結果になりました。
これは、引数の値を意味する %1 などの値は呼び出し先に干渉されない、ということでしょう。
今までは別のファイルを呼び出していたのですけど、CALL には自分自身に記載されているラベル部分を呼び出す機能を持っています。
call :ラベル名 引数群
使う感じとしてはいままでの別のバッチファイルを呼び出していたときと同じですけど、同じファイルを呼び出すためになんだか変な挙動に見えてしまうかもしれません。
たとえば、あえてややこしい例になってしまいますけど、次のようなバッチファイルを実行してみましょう。
■ test-call.bat
@echo off
call :JUMP_POINT %2 %1
:JUMP_POINT
echo %0: %%1 = %1
echo %0: %%2 = %2
先ほどまでの通りの引数を渡して呼び出しました。その結果は次の通りとなります。
:JUMP_POINT: %1 = test_b
:JUMP_POINT: %2 = test_a
test-call.bat: %1 = test_a
test-call.bat: %2 = test_b
とりあえず細かいところを抜かせば、結果は別のバッチファイルを CALL したときと同じで、引数の値はしっかりと交換されています。そして戻ってくるとまた元通りになっています。同じファイルなのにこのように変わるので注意です。
細かいことを見てみると、今回のバッチファイルでは同じ ECHO 行、といいますか :JUMP_POINT ラベル以下を CALL 前と CALL 後の両方で利用しています。
まず CALL が呼び出されて、:JUMP_POINT ラベルへ %2 %1 引数が渡されて呼び出されます。そして ECHO 行でそれらを表示したところでバッチファイルの終わりに達するので、CALL で呼び出されたもとの場所へ戻ります。表示されている引数は、CALL で逆順に指定したため、逆順です。
元の場所へ戻り、バッチファイルはそのまま処理を継続しています。そして :JUMP_POINT ラベル以下の実行に移ります。このとき引数の状態は本来の状態に戻っているので、本来渡された度折の順番で表示されています。そしてバッチファイルの終わりに達したため、全体として処理終了となります。
なお、大きなことではないですけど、CALL でラベル指定の呼び出しを行ったとき、%0 の値もコロンを含めたラベル名に変わっているところにも注意です。
実行を一時とめてみる
最初のほうで、処理が終了すると自動的にコンソールが閉じてしまう、と言いました。それを阻止する上でコンソールを立ち上げてからそこで実行する、というお話をしたのですけど、やはりすこし面倒だったりしますよね。
そこで、手軽なバッチファイルを一時停止するコマンドを紹介しておきます。
pause
これだけです。これを実行すると、画面に "続行するには何かキーを押してください . . ." と表示されて入力待ち状態となります。そして、文字通りなにかキーを押してあげると、処理が再開するという仕組みです。
これをバッチファイルの最後に書いてあげることで、キーが押されるまでウィンドウが閉じられずに済むようになります。
[ もどる ]