普段Linuxで開発作業している私がWindowsのバッチファイルというものを試してみました。以下不正確なところがあるかもしれませんが、Linuxとの違いや感想を交えつつ、コードの書き方を説明します。
バッチファイルの実行の仕方
「コマンドプロンプト」というのを開くとコマンドを実行できる。バッチファイルはファイル名が .bat
で終わっていればファイル名を指定してエンターするだけで実行できる。Linuxのように実行権限の設定とかはなさそう。また、Linuxと違ってカレントディレクトリにあるバッチファイルはPATHが通ってなくても ./
のようなものを付けなくてもファイル名だけで実行できる。
「コマンドプロンプト」はWindowsスタートメニューの「Windows システム ツール」の中にある。
先頭に書くおまじない
バッチファイルは実行時に実行するコマンドがいちいち表示される。Bashでいう set -x
の状態に近い。
set -x
を解除するには echo off
というコマンドを呼ぶ。またはコマンドの先頭に @
を付けると、そのコマンドだけ set -x
解除状態で実行される。バッチファイル全体で set -x
解除状態にしたい場合は、バッチファイルの先頭で echo off
を呼ぶことになるが、 echo off
と書くと echo off
と表示されてしまう。これを防ぐために @echo off
と最初に書くのがバッチファイルの習わしになる。
後述するが setlocal enabledelayedexpansion
という1行も最初のほうに書いておくのがよさそう。
Linuxのようなshebangはない。
@echo off
setlocal enabledelayedexpansion
コメント
REM
というコマンドがコメントを表すらしい。コメントがコマンド?!
REM ここはコメント
コマンドなので、上記 @echo off
がない場合は、コメントが実行時に表示されてしまう。
コマンドなので、行の途中からコメントを入れることもできない。
:
を行の先頭に書くと本来はラベルを表すらしいが、参照されなければなにも意味がないので、コメントの代わりに使うのもありらしい。
: ここはコメント
複数行のコメントは、ラベルでジャンプしてしまえばいいらしい。強引だ。
goto EOC
コメント1
コメント2
:EOC
変数
変数への代入は
set var1=Hello
ダブルクオーテーションで囲もうとしても、ダブルクオーテーション自体も文字列の一部として変数に含まれる。Bashとは違う。
set var1="Hello"
文字列の一部に空白があってもいいらしい。見た目がちょっと違和感あるが。
set var1=Hello World
=
の前後にスペースを置けないのはBashと同じだ。
変数の参照は変数名の先頭と最後に %
を付ける。
echo %var1%
変数が空だと変数展開されない様子。
set var1=
echo %var1%
%var1%
とそのまま表示されてしまう
ディレクトリ階層の区切り文字
ファイルやディレクトリを参照するときの区切り文字は /
ではなく \
。バックスラッシュは円記号で表示されるかもしれない。 ..
はLinuxと同じ意味で使える。Linuxでいう ls
は dir
だ。
dir ..\
if文
if %var1% == Hello (
echo World
)
括弧の中はif文判定前に変数展開されてしまうので、以下は期待した動きにはならない。
set var1=Hello
if %var1% == Hello (
set msg=World
echo %msg%
)
msgへの代入前にechoの引数を展開しようとして、その段階ではまだ変数が代入されていないので、 %msg%
とそのまま表示される
バッチファイルの中で setlocal enabledelayedexpansion
と宣言して、 !
で変数名を囲むと、if分岐の中も順番に変数展開してくれる。これは遅延変数と言うらしい。そんな特別な名前の付く機能でなくてもBashとかは普通に期待通りに動くのに。。。
setlocal enabledelayedexpansion
set var1=Hello
if %var1%==Hello (
set msg=World
echo !msg!
)
この場合は World
と表示される。 setlocal enabledelayedexpansion
はコマンドプロンプトで直接試そうとしてもできないようだ。バッチファイルに書くことで確認できた。
遅延変数はfor文でも必要になってくる。
面倒だから変数の参照は全部遅延変数にしてしまおうと思ったが、if文やfor文の外側では遅延変数は使えない様子。
日時
date
, time
という変数のようなものを参照すると、参照したときの日時が取得できる。以下は実行するごとにその時の時刻が表示される。
echo %time%
自分の環境ではこれで 17:47:10.90
のような表記で出力された。
以下のように書くと時刻の部分文字列を取得できる。
echo %time:~0,5%
これで 17:47
になる。夕方である。
次の例では分だけを得る。
echo %time:~3,2%
if文やfor文の中で時間かかる処理をする場合は時刻も !time!
と遅延変数のようにすることが必要。
ファイルへの書き込み
>
や >>
はBashと同じように使えるようだ。
echo Hello >> log.txt
最後の改行はWindowsらしく \r\n
の2バイトである。あと、なぜか最後改行直前に空白(\x20
)が入る。
後日追記:以下のように >>
の前を詰めれば空白は出力されないらしい。 >>
直前の空白が出力されるなんて意外すぎる。
echo Hello>> log.txt
もしくは書く順番を変える。
>> log.txt echo Hello
か、カッコで囲む。
(echo Hello) >> log.txt
for文
サンプル
for %%a in (111 222 333) do (
echo %%a
)
これで以下の出力になる。
111
222
333
%%a
というのがfor文の中だけで使える特別な変数。 %%
のあとに続くアルファベットは、なんと1文字しか使えない!
/f
というオプションでファイルの中身を行単位で実行できる。
input.txt
が以下の内容だとして、
111,aaa,xxx
222,bbb,yyy
/f
を使ってみる。
for /f %%a in (input.txt) do (
echo Line: %%a
)
これで以下のような出力になる。
Line: 111,aaa,xxx
Line: 222,bbb,yyy
/f "tokens=1,2,3 delims=,"
というオプションを書くと、行をカンマ区切りで3列分を取得してくれる。3列分の文字列がどこに保管されるかというと、 %%a
と書いたら %%a
, %%b
, %%c
というアルファベット順に3つの変数が勝手に作られる。 %%b
と書いたら %%b
, %%c
, %%d
になる。変態的だ。 %%z
と書いたらどうなるかはわからない。
for /f "tokens=1,2,3 delims=," %%a in (input.txt) do (
echo Col1: %%a
echo Col2: %%b
echo Col3: %%c
)
Col1: 111
Col2: aaa
Col3: xxx
Col1: 222
Col2: bbb
Col3: yyy
コマンドの終了コード
%ERRORLEVEL%
という変数で直前のコマンドの終了コードを得られる。0が正常で、それ以外は異常終了。Bashの $?
みたいなものか。
if文やfor文の中では遅延変数を使って !ERRORLEVEL!
としないと予期しないことになる。
コマンドの呼び出し方:サンプルとしてfindコマンド
find
というコマンドは、Linuxでいう find
ではなく grep
だ。指定のファイルから文字列を検索してマッチした行のみを出力できる。Linuxとは違って余計なお世話な出力があり機械処理はしづらい。
パラメータの指定の仕方がBashと違っていて、ちょっと予想外な動きをする。
echo Sample1
find xxx input.txt
echo Sample2
find "xxx" input.txt
出力はこうなる。パラメータのダブルクオートがあるかどうか区別している様子。Bashに慣れているとダブルクオートを外してからコマンドに渡すイメージだけど、コマンドプロンプトではコマンド側で解釈しているのだろうか。
Sample1
FIND: パラメーターの書式が違います
Sample2
---------- INPUT.TXT
111,aaa,xxx
後日追記:
ダブルクオートどころか、空白も含めてまるごとコマンドに渡されているっぽい。
findstr
というコマンドもあるらしい。あと、 where
というコマンドがLinuxでいう find
に近いらしい。