いまさらか!なんだけどWindowsバッチのはなし

それでもやっぱり時々は書かなきゃいけないWindowsバッチ。複雑なことはやらせるなという話なのかもしれないが、他のスクリプト実行環境をどうしても入れられない場合などは書くほかないのである。
あとちょいちょいかいてあるとおりにならないんだけどこれはなんでなのかな?

文字列を切り刻む

なん文字目から切ればいいということがわかっている場合はかんたんなのだが、そうでない場合はfor文まわしてdelimsでスプリットしていかねばならないらしい。しかもfor文の中のステートメントは先に展開されてしまうので、取得した文字列をこの配列に代入して…というのは案外難しいらしい。

set n=0
for /f "tokens=1-3 delims=_ " %%i in ('command') do @call :subset %%i %%j %%k

:subset
    set /a n+=1
    set data[%n%]=%1

けどこの場合echo data[n]とやりたい場合の書き方がよくわからなかったりするのである…素直にグローバルスコープの変数に入れて使いまわしたほうが良いのかもしれない(モダンなプログラムをかいている人には気持ち悪いと思うが)


ちなみにdelimsはデリミターのことだが、マニュアルには複数指定可、デフォルトはタブとスペースがきくとかいてある。じゃぁスペースとカンマで分けたい場合はどうするの?という場合は結構探したけど書いてなかった。やらないわけ無いと思うんだが…

正解は

for /f "tokens=* delims=, " ほにゃらら

スペース以外でも分割したい場合、スペースはパラメータの区切りとして認識されるので、そうされないために最後にdelimsを指定すること。ほいでもってdelims= ,とするとカンマがわけわかりませんょといわれてしまうので、delims=, である。エスケープ使えればいいんだけどね

ループに引数をわたす

:subset %%i %%j

このブロック名の後ろにいる人が引数。使うときは番号で呼ぶ。

:subset
    set result=%1
    set value=%2

ブロックというかメソッドと思えば良いらしい

ブロックを出てからの挙動

ふつうの文が最後の場合はいいのだが、for文の場合は一旦forの終端に戻り、そのままシーケンシャルに実行されるので、ブロックの最後に次に行きたいブロックの場所を指定しておかねばならない。つまり

for /f "token=1,2 delims=,; " %%a in (test.txt) do @call :nextLoop %%a %%b


:nextLoop
set a=%1
for /f "token=1,2 delims= " %%i in ("%a%") do @call :nextnextLoop %%i %%j

とした場合、最初にメインループ→nextLoop→もとのループにもどる→もう一回nextLoopとなるようだ。呼び出し元に戻ってもらいたいものだがそういえばプログラムとはそういうものだった。