forコマンドは色々なことができます。バッチファイルで仕事をしなければいけないのなら是非覚えちゃいましょう。難しいですが強力です。
今はもうOSに標準でPowerShellが搭載されている時代です。Windows 2000世代からはWSHが標準搭載されバッチファイルから解放されました。Windows 7, Windows Server 2008ではPowerShellが標準搭載されWSHからも解放されました。.net frameworkも使いつつオブジェクト指向のスクリプトが書ける、そんないい時代になりました。めでたしめでたし…とはいかないのがこの業界です。残念ながら最新のOS上でいまだにバッチファイルを書かなければならない仕事、未だに仮想化されたWindowsNTの面倒を見なければいけない仕事などが存在するのが現実です。
バッチファイルでは実際のところ相当生産性が低いのですが、そんな中で異彩を放つコマンド”for”。その名前からはただの繰り返しコマンドに思えるのですが、実は恐ろしい力を秘めています。実際の所バッチファイルでちょっと複雑なことをやろうと思うとforを効果的に使わないといけません。今回は未だにバッチファイルを書かなければいけない不幸な人のためにforコマンドのTips集をお届けします。
forコマンドのヘルプから読み取れる「できること」
forコマンドは色々な事ができるコマンドなのですが、まじまじとヘルプを見たことが無い人も多いと思います。以下はWindows7のforコマンドのヘルプです。
指定されたコマンドをファイル セットの各ファイルに対して実行します。
FOR %変数 IN (セット) DO コマンド [コマンドパラメーター]
%変数 単一文字の置き換え可能なパラメーターを指定します。
(セット) ファイル セットを指定します。ワイルドカードを使用できます。
コマンド 各ファイルごとに実行するコマンドを指定します。
コマンドパラメーター
指定されたコマンドのパラメーターまたはスイッチを指定します。
バッチ プログラムで FOR コマンドを使用するときは、%変数の代わりに、
%%変数を使用してください。変数名では大文字と小文字が区別されるため、
%i と %I は異なります。
コマンド拡張機能を有効にすると、次の FOR コマンドの追加形式
がサポートされるようになります:
FOR /D %変数 IN (セット) DO コマンド [コマンド パラメーター]
セットがワイルドカードを含む場合は、ファイル名ではなくディレクトリ名
の一致を指定します。
FOR /R [[ドライブ:]パス] %変数 IN (セット) DO コマンド [コマンド パラメーター]
[ドライブ:]パスから始めて、ツリーの各ディレクトリで FOR 文を実行し
ます。/R の後にディレクトリが指定されていない場合は、現在の
ディレクトリが使用されます。セットが単一のピリオド (.) である場合は、
ディレクトリ ツリーの列挙だけを行います。
FOR /L %変数 IN (開始,ステップ,終了) DO コマンド [コマンド パラメーター]
セットは、ステップの量ごとに変化する開始から終了までの数列です。
たとえば、(1,1,5) は 1 2 3 4 5、(5,-1,1) は (5 4 3 2 1) という数列に
なります。
FOR /F ["オプション"] %変数 IN (ファイル セット) DO コマンド
[コマンド パラメーター]
FOR /F ["オプション"] %変数 IN ("文字列") DO コマンド [コマンド パラメーター]
FOR /F ["オプション"] %変数 IN ('コマンド') DO コマンド [コマンド パラメーター]
または usebackq オプションの場合:
FOR /F ["オプション"] %変数 IN (ファイル セット) DO コマンド
[コマンド パラメーター]
FOR /F ["オプション"] %変数 IN ('文字列') DO コマンド [コマンド パラメーター]
FOR /F ["オプション"] %変数 IN (`コマンド`) DO コマンド [コマンド パラメーター]
ファイル セットは、1 つ以上のファイル名です。各ファイルが開かれ、
読み取られ、処理されてから、ファイル セットの次のファイルに進みます。
処理では、ファイルの読み取り、個々のテキスト行への分割と、0 個以上の
トークンへの解析が行われます。その後、見つかったトークン文字列を変数値に
設定して for ループの本体が呼び出されます。既定では、/F は、各ファイルの
各行から、空白で区切られた最初のトークンを取得して渡します。空白行は
スキップされます。既定の解析動作を変更するには、省略可能な "オプション"
パラメーターを指定します。これは、異なる解析オプションを指定する 1 つ以上の
キーワードを含む、引用符で囲まれた文字列です。キーワードは、次のとおりです:
eol=c - 行末のコメント文字を指定します (1 文字)。
skip=n - ファイルの先頭でスキップする行数を指定します。
delims=xxx - 区切り文字のセットを指定します。
これは、既定の区切り文字であるスペースとタブを
置き換えます。
tokens=x,y,m-n - 各繰り返しに対して、各行から for 本体に渡す
トークンを指定します。これにより、追加の変数名が
割り当てられます。
m-n の形式は範囲で、m 番目から n 番目の
トークンを指定します。
tokens= 文字列の最後の文字がアスタリスクである場合は、
追加の変数が割り当てられ、最後のトークンが解析された
後、行に含まれている残りのテキストを受け取ります。
usebackq - 次の新しい表示形式を指定します。
逆引用符で囲まれた文字列がコマンドとして実行され、
一重引用符で囲まれた文字列がリテラル文字列コマンドに
なり、ファイル セットのファイル名を二重引用符で
囲めるようになります。
例を参考にしてください:
FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do @echo %i %j %k
この例は、myfile.txt の各行を解析します。セミコロンで始まる行を無視し、
各行の 2 番目と 3 番目のトークンを for 本体に渡します。
トークンは、コンマまたはスペースで区切られています。
for 本体の文が %i で 2 番目のトークンを、%j で 3 番目のトークンを取得し、
%k で 3 番目以降のすべてのトークンを取得していることに
注意してください。
スペースを含むファイル名に対しては、二重引用符でファイル名を引用する
必要があります。
この方法で二重引用符を使うためには、usebackq オプションも
使わなければなりません。
使わなければ、二重引用符はリテラル文字列の定義として
解釈され、解析されます。
%i は for 文で明示的に宣言され、%j と %k は tokens= オプションで暗黙的に
宣言されています。
tokens= 行を使って 26 個までのトークンを指定できますが、
文字 'z' または 'Z' よりも高い変数を宣言することはできません。FOR 変数名は
単一の文字で、大文字と小文字を区別し、グローバルなものであり、一度に
アクティブにできるのは合計 52 個までです。
また、かっこで囲んだファイル セットを一重引用符で囲み、文字列にすることに
より、即時の文字列に対する FOR /F 解析ロジックを使うこともできます。
これは、ファイルからの単一入力行として処理されます。
最後に、FOR /F コマンドを使って、コマンド出力を解析することができます。
かっこの中のファイル セットを逆引用符で囲みます。この文字列は、コマンド
ラインとして子 CMD.EXE に渡されます。出力はメモリにキャプチャされ、
ファイルのように解析されます。
例:
FOR /F "usebackq delims==" %i IN (`set`) DO @echo %i
この例は、現在の環境の環境変数名を列挙します。
また、FOR 変数参照の置換も拡張されました。
次のオプション構文を使うことができます:
%~I - すべての引用句 (") を削除して、%I を展開します。
%~fI - %I を完全修飾パス名に展開します。
%~dI - %I をドライブ文字だけに展開します。
%~pI - %I をパス名だけに展開します。
%~nI - %I をファイル名だけに展開します。
%~xI - %I をファイル拡張子だけに展開します。
%~sI - 展開されたパスは短い名前だけを含みます。
%~aI - %I をファイルの属性に展開します。
%~tI - %I ファイルの日付/時刻に展開します。
%~zI - %I ファイルのサイズに展開します。
%~$PATH:I - PATH 環境変数に指定されているディレクトリを
検索し、最初に見つかった完全修飾名に %I を
展開します。
環境変数名が定義されていない場合、または検索
してもファイルが見つからなかった場合は、この
修飾子を指定すると空の文字列に展開されます。
修飾子を組み合わせて、複合結果を得ることもできます:
%~dpI - %I をドライブ文字とパスだけに展開します。
%~nxI - %I をファイル名と拡張子だけに展開します。
%~fsI - %I を完全なパスと短い名前だけに展開します。
%~dp$PATH:I - PATH 環境変数に指定されているディレクトリを
検索して %I を探し、最初に見つかったファイル
のドライブ文字とパスだけに展開します。
%~ftzaI - %I を DIR コマンドの出力行のように展開します。
上の例の %I と PATH は、他の有効な値で置き換えることができます。
%~ 構文は、有効な FOR 変数名によって区切られます。%I のような大
文字の変数を使うと読み取りやすく、大文字と小文字を区別しない修飾子
との混乱を避けることができます。
ヘルプの長さにまず驚かされますが、「指定されたコマンドをファイル セットの各ファイルに対して実行します。」と、ある通り、基本的には複数のファイルに対してコマンドを実行することが出来るコマンドということになっているようです。
FOR /D %変数 IN (セット) DO コマンド [コマンド パラメーター]
セットがワイルドカードを含む場合は、ファイル名ではなくディレクトリ名
の一致を指定します。
さらに上記のようにディレクトリに対しても処理を行えることがわかります。
FOR /R [[ドライブ:]パス] %変数 IN (セット) DO コマンド [コマンド パラメーター]
[ドライブ:]パスから始めて、ツリーの各ディレクトリで FOR 文を実行し
ます。/R の後にディレクトリが指定されていない場合は、現在の
ディレクトリが使用されます。セットが単一のピリオド (.) である場合は、
ディレクトリ ツリーの列挙だけを行います。
ディレクトリツリーに対して再帰的に処理も行えるようです。
FOR /L %変数 IN (開始,ステップ,終了) DO コマンド [コマンド パラメーター]
セットは、ステップの量ごとに変化する開始から終了までの数列です。
たとえば、(1,1,5) は 1 2 3 4 5、(5,-1,1) は (5 4 3 2 1) という数列に
なります。
もちろん普通のプログラム言語のように数を変化させながら繰り返し処理もできます。
FOR /F ["オプション"] %変数 IN (ファイル セット) DO コマンド
[コマンド パラメーター]
FOR /F ["オプション"] %変数 IN ("文字列") DO コマンド [コマンド パラメーター]
FOR /F ["オプション"] %変数 IN ('コマンド') DO コマンド [コマンド パラメーター]
または usebackq オプションの場合:
FOR /F ["オプション"] %変数 IN (ファイル セット) DO コマンド
[コマンド パラメーター]
FOR /F ["オプション"] %変数 IN ('文字列') DO コマンド [コマンド パラメーター]
FOR /F ["オプション"] %変数 IN (`コマンド`) DO コマンド [コマンド パラメーター]
文字列やコマンドの結果に対しても処理ができちゃいます。
ファイル セットは、1 つ以上のファイル名です。各ファイルが開かれ、
読み取られ、処理されてから、ファイル セットの次のファイルに進みます。
処理では、ファイルの読み取り、個々のテキスト行への分割と、0 個以上の
トークンへの解析が行われます。その後、見つかったトークン文字列を変数値に
設定して for ループの本体が呼び出されます。既定では、/F は、各ファイルの
各行から、空白で区切られた最初のトークンを取得して渡します。空白行は
スキップされます。
さらにファイルに対してはその内容を読み取り行ごとに処理したり、さらに”トークンへの解析”までしてくれちゃいます。
既定の解析動作を変更するには、省略可能な "オプション"
パラメーターを指定します。これは、異なる解析オプションを指定する 1 つ以上の
キーワードを含む、引用符で囲まれた文字列です。キーワードは、次のとおりです:
eol=c - 行末のコメント文字を指定します (1 文字)。
skip=n - ファイルの先頭でスキップする行数を指定します。
delims=xxx - 区切り文字のセットを指定します。
これは、既定の区切り文字であるスペースとタブを
置き換えます。
tokens=x,y,m-n - 各繰り返しに対して、各行から for 本体に渡す
トークンを指定します。これにより、追加の変数名が
割り当てられます。
m-n の形式は範囲で、m 番目から n 番目の
トークンを指定します。
tokens= 文字列の最後の文字がアスタリスクである場合は、
追加の変数が割り当てられ、最後のトークンが解析された
後、行に含まれている残りのテキストを受け取ります。
usebackq - 次の新しい表示形式を指定します。
逆引用符で囲まれた文字列がコマンドとして実行され、
一重引用符で囲まれた文字列がリテラル文字列コマンドに
なり、ファイル セットのファイル名を二重引用符で
囲めるようになります。
コメント文字の指定や、スキップする行数の指定まで出来ますし、区切り文字の指定、本体に渡すトークン指定までできちゃいます。
また、FOR 変数参照の置換も拡張されました。
次のオプション構文を使うことができます:
%~I - すべての引用句 (") を削除して、%I を展開します。
%~fI - %I を完全修飾パス名に展開します。
%~dI - %I をドライブ文字だけに展開します。
%~pI - %I をパス名だけに展開します。
%~nI - %I をファイル名だけに展開します。
%~xI - %I をファイル拡張子だけに展開します。
%~sI - 展開されたパスは短い名前だけを含みます。
%~aI - %I をファイルの属性に展開します。
%~tI - %I ファイルの日付/時刻に展開します。
%~zI - %I ファイルのサイズに展開します。
%~$PATH:I - PATH 環境変数に指定されているディレクトリを
検索し、最初に見つかった完全修飾名に %I を
展開します。
環境変数名が定義されていない場合、または検索
してもファイルが見つからなかった場合は、この
修飾子を指定すると空の文字列に展開されます。
修飾子を組み合わせて、複合結果を得ることもできます:
%~dpI - %I をドライブ文字とパスだけに展開します。
%~nxI - %I をファイル名と拡張子だけに展開します。
%~fsI - %I を完全なパスと短い名前だけに展開します。
%~dp$PATH:I - PATH 環境変数に指定されているディレクトリを
検索して %I を探し、最初に見つかったファイル
のドライブ文字とパスだけに展開します。
%~ftzaI - %I を DIR コマンドの出力行のように展開します。
さらにさらに変数の置換機能まであります。
…。さすがはforコマンドですね。「それは別のコマンドを用意したほうがいいんじゃないのか?」という機能まで満載されています。コマンドプロンプト、バッチファイルでは他の機能が貧弱なだけにforコマンドを多用することになります。
では、いくつか例を実行してみたいと思います。この手のものは「習うより慣れろ」ですから是非コマンドプロンプトを立ち上げて一緒に操作しながら読んでもらえればと思います。
ループ処理
まずは、テスト用のディレクトリを作成します。
1: mkdir c:fortest
2: cd c:fortest
テスト用のテキストファイルを用意しましょう。適当に10個程度にしましょうか。せっかくですのでこれもforコマンドで作成してみましょう。
以下のように実行すると10個のテキストファイルが生成されます。
1: c:fortest>for /L %i IN (1,1,10) DO echo sample%i > sample%i.txt
2:
3: c:fortest>echo sample1 1>sample1.txt
4:
5: c:fortest>echo sample2 1>sample2.txt
6:
7: c:fortest>echo sample3 1>sample3.txt
8:
9: c:fortest>echo sample4 1>sample4.txt
10:
11: c:fortest>echo sample5 1>sample5.txt
12:
13: c:fortest>echo sample6 1>sample6.txt
14:
15: c:fortest>echo sample7 1>sample7.txt
16:
17: c:fortest>echo sample8 1>sample8.txt
18:
19: c:fortest>echo sample9 1>sample9.txt
20:
21: c:fortest>echo sample10 1>sample10.txt
/Lオプションを使用するとこのように変数の中に数字を入れつつ、コマンドを繰り返し実行させることができます。この例では変数%iに対して1から1つづつ値をふやしながら10になるまで「echo sample%1 > sample%i.txt」というコマンドを実行させています。これはいわゆるプログラム言語でいうところのfor文と同じような感じなので理解しやすいのではないかと思います。
ファイル名の取得
次はファイル名の取得の例です。以下のように実行できます。
1: c:fortest>for %i IN (*.txt) DO @echo %i
2: sample1.txt
3: sample10.txt
4: sample2.txt
5: sample3.txt
6: sample4.txt
7: sample5.txt
8: sample6.txt
9: sample7.txt
10: sample8.txt
11: sample9.txt
オプションを付けずにforを実行すると、パターンにマッチするファイル名を取得して変数に1つづつ値を入れつつ、コマンドを実行させることができます。このパターンマッチにはディレクトリ(フォルダ)はマッチしません。以下のように確認できます。
1: c:fortest>for /L %i IN (1,1,10) DO mkdir dir%i
2:
3: c:fortest>mkdir dir1
4:
5: c:fortest>mkdir dir2
6:
7: c:fortest>mkdir dir3
8:
9: c:fortest>mkdir dir4
10:
11: c:fortest>mkdir dir5
12:
13: c:fortest>mkdir dir6
14:
15: c:fortest>mkdir dir7
16:
17: c:fortest>mkdir dir8
18:
19: c:fortest>mkdir dir9
20:
21: c:fortest>mkdir dir10
22:
23: c:fortest>for %i IN (*) DO @echo %i
24: sample1.txt
25: sample10.txt
26: sample2.txt
27: sample3.txt
28: sample4.txt
29: sample5.txt
30: sample6.txt
31: sample7.txt
32: sample8.txt
33: sample9.txt
34:
35: c:fortest>dir
36: ドライブ C のボリューム ラベルがありません。
37: ボリューム シリアル番号は C0AF-1B4F です
38:
39: c:fortest のディレクトリ
40:
41: 2012/03/15 21:59 <DIR> .
42: 2012/03/15 21:59 <DIR> ..
43: 2012/03/15 21:59 <DIR> dir1
44: 2012/03/15 21:59 <DIR> dir10
45: 2012/03/15 21:59 <DIR> dir2
46: 2012/03/15 21:59 <DIR> dir3
47: 2012/03/15 21:59 <DIR> dir4
48: 2012/03/15 21:59 <DIR> dir5
49: 2012/03/15 21:59 <DIR> dir6
50: 2012/03/15 21:59 <DIR> dir7
51: 2012/03/15 21:59 <DIR> dir8
52: 2012/03/15 21:59 <DIR> dir9
53: 2012/03/15 21:55 10 sample1.txt
54: 2012/03/15 21:55 11 sample10.txt
55: 2012/03/15 21:55 10 sample2.txt
56: 2012/03/15 21:55 10 sample3.txt
57: 2012/03/15 21:55 10 sample4.txt
58: 2012/03/15 21:55 10 sample5.txt
59: 2012/03/15 21:55 10 sample6.txt
60: 2012/03/15 21:55 10 sample7.txt
61: 2012/03/15 21:55 10 sample8.txt
62: 2012/03/15 21:55 10 sample9.txt
63: 10 個のファイル 101 バイト
64: 12 個のディレクトリ 142,859,378,688 バイトの空き領域
ディレクトリ名の取得
/Dオプションをつけて実行すると、ディレクトリのみがパターンにマッチするようになります。
1: c:fortest>for /D %i IN (*) DO @echo %i
2: dir1
3: dir10
4: dir2
5: dir3
6: dir4
7: dir5
8: dir6
9: dir7
10: dir8
11: dir9
再帰処理
ディレクトリを指定し、そこから下位のディレクトリのそれぞれでforコマンドを実行させることができます。ちょっとディレクトリ、ファイルを準備してみます。
1: c:fortest>for /L %i IN (1,1,10) DO echo sample%i > .dir%isample%i.txt
2:
3: c:fortest>echo sample1 1>.dir1sample1.txt
4:
5: c:fortest>echo sample2 1>.dir2sample2.txt
6:
7: c:fortest>echo sample3 1>.dir3sample3.txt
8:
9: c:fortest>echo sample4 1>.dir4sample4.txt
10:
11: c:fortest>echo sample5 1>.dir5sample5.txt
12:
13: c:fortest>echo sample6 1>.dir6sample6.txt
14:
15: c:fortest>echo sample7 1>.dir7sample7.txt
16:
17: c:fortest>echo sample8 1>.dir8sample8.txt
18:
19: c:fortest>echo sample9 1>.dir9sample9.txt
20:
21: c:fortest>echo sample10 1>.dir10sample10.txt
22:
23: c:fortest>dir /s
24: ドライブ C のボリューム ラベルがありません。
25: ボリューム シリアル番号は C0AF-1B4F です
26:
27: c:fortest のディレクトリ
28:
29: 2012/03/15 21:59 <DIR> .
30: 2012/03/15 21:59 <DIR> ..
31: 2012/03/15 22:05 <DIR> dir1
32: 2012/03/15 22:05 <DIR> dir10
33: 2012/03/15 22:05 <DIR> dir2
34: 2012/03/15 22:05 <DIR> dir3
35: 2012/03/15 22:05 <DIR> dir4
36: 2012/03/15 22:05 <DIR> dir5
37: 2012/03/15 22:05 <DIR> dir6
38: 2012/03/15 22:05 <DIR> dir7
39: 2012/03/15 22:05 <DIR> dir8
40: 2012/03/15 22:05 <DIR> dir9
41: 2012/03/15 21:55 10 sample1.txt
42: 2012/03/15 21:55 11 sample10.txt
43: 2012/03/15 21:55 10 sample2.txt
44: 2012/03/15 21:55 10 sample3.txt
45: 2012/03/15 21:55 10 sample4.txt
46: 2012/03/15 21:55 10 sample5.txt
47: 2012/03/15 21:55 10 sample6.txt
48: 2012/03/15 21:55 10 sample7.txt
49: 2012/03/15 21:55 10 sample8.txt
50: 2012/03/15 21:55 10 sample9.txt
51: 10 個のファイル 101 バイト
52:
53: c:fortestdir1 のディレクトリ
54:
55: 2012/03/15 22:05 <DIR> .
56: 2012/03/15 22:05 <DIR> ..
57: 2012/03/15 22:05 10 sample1.txt
58: 1 個のファイル 10 バイト
59:
60: c:fortestdir10 のディレクトリ
61:
62: 2012/03/15 22:05 <DIR> .
63: 2012/03/15 22:05 <DIR> ..
64: 2012/03/15 22:05 11 sample10.txt
65: 1 個のファイル 11 バイト
66:
67: c:fortestdir2 のディレクトリ
68:
69: 2012/03/15 22:05 <DIR> .
70: 2012/03/15 22:05 <DIR> ..
71: 2012/03/15 22:05 10 sample2.txt
72: 1 個のファイル 10 バイト
73:
74: c:fortestdir3 のディレクトリ
75:
76: 2012/03/15 22:05 <DIR> .
77: 2012/03/15 22:05 <DIR> ..
78: 2012/03/15 22:05 10 sample3.txt
79: 1 個のファイル 10 バイト
80:
81: c:fortestdir4 のディレクトリ
82:
83: 2012/03/15 22:05 <DIR> .
84: 2012/03/15 22:05 <DIR> ..
85: 2012/03/15 22:05 10 sample4.txt
86: 1 個のファイル 10 バイト
87:
88: c:fortestdir5 のディレクトリ
89:
90: 2012/03/15 22:05 <DIR> .
91: 2012/03/15 22:05 <DIR> ..
92: 2012/03/15 22:05 10 sample5.txt
93: 1 個のファイル 10 バイト
94:
95: c:fortestdir6 のディレクトリ
96:
97: 2012/03/15 22:05 <DIR> .
98: 2012/03/15 22:05 <DIR> ..
99: 2012/03/15 22:05 10 sample6.txt
100: 1 個のファイル 10 バイト
101:
102: c:fortestdir7 のディレクトリ
103:
104: 2012/03/15 22:05 <DIR> .
105: 2012/03/15 22:05 <DIR> ..
106: 2012/03/15 22:05 10 sample7.txt
107: 1 個のファイル 10 バイト
108:
109: c:fortestdir8 のディレクトリ
110:
111: 2012/03/15 22:05 <DIR> .
112: 2012/03/15 22:05 <DIR> ..
113: 2012/03/15 22:05 10 sample8.txt
114: 1 個のファイル 10 バイト
115:
116: c:fortestdir9 のディレクトリ
117:
118: 2012/03/15 22:05 <DIR> .
119: 2012/03/15 22:05 <DIR> ..
120: 2012/03/15 22:05 10 sample9.txt
121: 1 個のファイル 10 バイト
122:
123: ファイルの総数:
124: 20 個のファイル 202 バイト
125: 32 個のディレクトリ 142,869,659,648 バイトの空き領域
これで、カレントディレクトリにはディレクトリが10個、ファイルが10個。サブディレクトリにはそれぞれファイルが1個づつという状態になりました。現在のディレクトリ以下にある.txtという拡張子を持つファイルを全て表示してみます。
1: c:fortest>for /R %i IN (*.txt) DO @echo %i
2: c:fortestsample1.txt
3: c:fortestsample10.txt
4: c:fortestsample2.txt
5: c:fortestsample3.txt
6: c:fortestsample4.txt
7: c:fortestsample5.txt
8: c:fortestsample6.txt
9: c:fortestsample7.txt
10: c:fortestsample8.txt
11: c:fortestsample9.txt
12: c:fortestdir1sample1.txt
13: c:fortestdir10sample10.txt
14: c:fortestdir2sample2.txt
15: c:fortestdir3sample3.txt
16: c:fortestdir4sample4.txt
17: c:fortestdir5sample5.txt
18: c:fortestdir6sample6.txt
19: c:fortestdir7sample7.txt
20: c:fortestdir8sample8.txt
21: c:fortestdir9sample9.txt
ディレクトリ名だけ表示するには以下のようにします。
1: c:fortest>for /R %i IN (.) DO @echo %i
2: c:fortest.
3: c:fortestdir1.
4: c:fortestdir10.
5: c:fortestdir2.
6: c:fortestdir3.
7: c:fortestdir4.
8: c:fortestdir5.
9: c:fortestdir6.
10: c:fortestdir7.
11: c:fortestdir8.
12: c:fortestdir9.
カレントディレクトリ(.)にだけマッチさせるような感じですね。
ファイルの中身へのアクセス
さて、次はいよいよファイルの中身へのアクセスです。これは/Fオプションで実現できます。
まず、テストのためにファイル内に10行書かれているファイルを用意します。これもfor文でやりましょう。
1: c:fortest>for /L %i IN (1,1,10) DO echo %i行目 >> sample.txt
2:
3: c:fortest>echo 1行目 1>>sample.txt
4:
5: c:fortest>echo 2行目 1>>sample.txt
6:
7: c:fortest>echo 3行目 1>>sample.txt
8:
9: c:fortest>echo 4行目 1>>sample.txt
10:
11: c:fortest>echo 5行目 1>>sample.txt
12:
13: c:fortest>echo 6行目 1>>sample.txt
14:
15: c:fortest>echo 7行目 1>>sample.txt
16:
17: c:fortest>echo 8行目 1>>sample.txt
18:
19: c:fortest>echo 9行目 1>>sample.txt
20:
21: c:fortest>echo 10行目 1>>sample.txt
22:
23: c:fortest>type sample.txt
24: 1行目
25: 2行目
26: 3行目
27: 4行目
28: 5行目
29: 6行目
30: 7行目
31: 8行目
32: 9行目
33: 10行目
このファイルの中身を1行づつ読み取ってみます。
1: c:fortest>for /F %i IN (sample.txt) DO @echo %i
2: 1行目
3: 2行目
4: 3行目
5: 4行目
6: 5行目
7: 6行目
8: 7行目
9: 8行目
10: 9行目
11: 10行目
「type sample.txt」を実行した結果と同じ結果になるわけですが、1行づつ読み取り、それが変数%iに入れられ、それが1行づつechoされているのがわかりますでしょうか。
もうちょっとわかりやすくするために以下のようにしてみましょうか。
1: c:fortest>for /F %i IN (sample.txt) DO @echo %iを出力しています。
2: 1行目を出力しています。
3: 2行目を出力しています。
4: 3行目を出力しています。
5: 4行目を出力しています。
6: 5行目を出力しています。
7: 6行目を出力しています。
8: 7行目を出力しています。
9: 8行目を出力しています。
10: 9行目を出力しています。
11: 10行目を出力しています。
あまり内容は変わっていませんが、変数の中にファイルの内容が1行づつ入っていく様子がわかるかと思います。
ちなみに/Fオプションを使っているときにはパターンマッチは行えません。つまり内容を読み込んで処理できるのは1ファイルだということです。
1: c:fortest>for /F %i IN (*.txt) DO @echo %iを出力しています。
2: ファイル *.txt が見つかりません。
文字列の処理、コマンドの結果
上記の/Fオプションはファイルの中身に対してだけではなくて、文字列に対してやコマンドの結果に対しても同じように処理できます。
1: c:fortest>for /F %i IN ("hogehoge") DO @echo %i
2: hogehoge
これだとただ単にechoしているのと変わらないのですが(どう嬉しいのかはもうちょっと待ってください)。文字列は変数に入れておくこともできます。
1: c:fortest>set mojiretsu=hogehoge
2:
3: c:fortest>echo %mojiretsu%
4: hogehoge
5:
6: c:fortest>for /F %i IN ("%mojiretsu%") DO @echo %i
7: hogehoge
コマンドの結果を処理する場合には「’’」を使ってコマンドを囲むことで実現できます。
1: c:fortest>for /F %i IN ('ipconfig') DO @echo %i
2: Windows
3: Wireless
4: 接続固有の
5: リンクローカル
6: IPv4
7: サブネット
8: デフォルト
9: イーサネット
10: メディアの状態.
11: 接続固有の
12: Tunnel
13: メディアの状態.
14: 接続固有の
15: Tunnel
16: メディアの状態.
17: 接続固有の
18: Tunnel
19: 接続固有の
20: IPv6
21: リンクローカル
22: デフォルト
23: Tunnel
24: メディアの状態.
25: 接続固有の
26: Tunnel
27: メディアの状態.
28: 接続固有の
おや。出力がipconfigの結果そのままではありませんね。これは規定の状態で以下のようになっているからです。(ヘルプより引用)
既定では、/F は、各ファイルの各行から、空白で区切られた最初のトークンを取得して渡します。空白行はスキップされます。
空白行はスキップされた上で、空白で区切っているわけです。もう一度単純な文字列を処理する例を見ましょう。
1: c:fortest>for /F %i IN ("hoge1 hoge2") DO @echo %i
2: hoge1
文字列は空白で区切られているので1つめのトークンの”hoge1”が%iに入れられています。2つめのトークンである”hoge2”は単純に捨てられています。これも受け取りたい場合にはtokensオプションを指定します。
1: c:fortest>for /F "tokens=*" %i IN ("hoge1 hoge2") DO @echo %i
2: hoge1 hoge2
1: c:fortest>for /F "tokens=*" %i IN ('ipconfig') DO @echo %i
2: Windows IP 構成
3: Wireless LAN adapter wlan:
4: 接続固有の DNS サフィックス . . . :
5: リンクローカル IPv6 アドレス. . . . : fe80::8d7e:8925:9d7e:38a2%18
6: IPv4 アドレス . . . . . . . . . . : 192.168.1.108
7: サブネット マスク . . . . . . . . : 255.255.255.0
8: デフォルト ゲートウェイ . . . . . : 192.168.1.1
9: イーサネット アダプター lan:
10: メディアの状態. . . . . . . . . . : メディアは接続されていません
11: 接続固有の DNS サフィックス . . . : test.local
12: Tunnel adapter isatap.jbs.local:
13: メディアの状態. . . . . . . . . . : メディアは接続されていません
14: 接続固有の DNS サフィックス . . . :
15: Tunnel adapter 6TO4 Adapter:
16: メディアの状態. . . . . . . . . . : メディアは接続されていません
17: 接続固有の DNS サフィックス . . . :
18: Tunnel adapter ローカル エリア接続* 14:
19: 接続固有の DNS サフィックス . . . :
20: IPv6 アドレス . . . . . . . . . . . : 2001:0:4137:9e76:1cca:16f7:2518:641c
21: リンクローカル IPv6 アドレス. . . . : fe80::1cca:16f7:2518:641c%15
22: デフォルト ゲートウェイ . . . . . : ::
23: Tunnel adapter isatap.{0A10F0FC-9ABB-42D0-8C73-C71B2976D3B0}:
24: メディアの状態. . . . . . . . . . : メディアは接続されていません
25: 接続固有の DNS サフィックス . . . :
26: Tunnel adapter Reusable Microsoft 6To4 Adapter:
27: メディアの状態. . . . . . . . . . : メディアは接続されていません
28: 接続固有の DNS サフィックス . . . :
上記の2つの例では「tokens=*」とオプションを指定することで空白で区切ることをせず、行全部を1つのトークンにするように指定しました。なので、1行全てが%iに入っています。
空白では区切りつつ、2つの別々の変数で受け取る場合には以下の用にします。
1: c:fortest>for /F "tokens=1,2" %i IN ("hoge1 hoge2") DO @echo %i %j
2: hoge1 hoge2
「tokens=1,2」と指定することで1番目と2番目のトークンをそれぞれ%i, %jに入力しています。%iは明示的に宣言されているのですが、%jはいきなり登場しています。これはもうこういうものだと思ってしまうしか無いと思います。%iからはじめるなら、%i, %j, %k …と順番になります。
もちろんtokenの選び方は自由です。
1: c:fortest>for /F "tokens=1,3" %i IN ("hoge1 hoge2 hoge3") DO @echo %i %j
2: hoge1 hoge3
3:
上記のように1番目と3番目を取得するようなこともできます。
1: c:fortest>for /F "tokens=1,3*" %i IN ("hoge1 hoge2 hoge3 hoge4 hoge5 hoge6") DO @echo %i %j - %k
2: hoge1 hoge3 - hoge4 hoge5 hoge6
上記のように*を使うとその後のすべての文字が次の変数(ここでは%k)に入ります。*の前には「,」は必要ありません。(ちょっと文法的に違和感がありますが・・・)
1: c:fortest>for /F "tokens=1-5" %i IN ("hoge1 hoge2 hoge3 hoge4 hoge5") DO @echo %i %j %k %l %m
2: hoge1 hoge2 hoge3 hoge4 hoge5
3:
4: c:fortest>for /F "tokens=1,2,3,4,5" %i IN ("hoge1 hoge2 hoge3 hoge4 hoge5") DO @echo %i %j %k %l %m
5: hoge1 hoge2 hoge3 hoge4 hoge5
上記のように”token=1-5”のように範囲で書くこともできます。この場合”tokens=1,2,3,4,5”と記述したのと全く同じ意味になります。
次に区切り文字です。規定では空白ですが、delimsオプションで指定することができます。
1: c:fortest>for /F "tokens=1,2,3 delims=," %i IN ("hoge1,hoge2,hoge3") DO @echo %i %j %k %l %m
2: hoge1 hoge2 hoge3 %l %m
これは,(カンマ)で区切った例です。これを使えばCSVファイルを扱えるわけです。
CSVファイルの読み込み
簡単なCSVファイルを準備します。
1: c:fortest>echo 1-1,1-2,1-3,1-4,1-5 > sample.txt
2:
3: c:fortest>echo 2-1,2-2,2-3,2-4,2-5 >> sample.txt
4:
5: c:fortest>echo 3-1,3-2,3-3,3-4,3-5 >> sample.txt
6:
7: c:fortest>type sample.txt
8: 1-1,1-2,1-3,1-4,1-5
9: 2-1,2-2,2-3,2-4,2-5
10: 3-1,3-2,3-3,3-4,3-5
このCSVを1行づつ読み込み、カンマで区切り、5つの変数にそれぞれ値を代入し、表示します。
1: c:fortest>for /F "tokens=1,2,3,4,5 delims=," %i IN (sample.txt) DO @echo %i %j %k %l %m
2: 1-1 1-2 1-3 1-4 1-5
3: 2-1 2-2 2-3 2-4 2-5
4: 3-1 3-2 3-3 3-4 3-5
CSVファイルにコメント行があり読み飛ばしたい時にはeolオプションが使えます。
テスト用のCSVファイルを用意します。
1: c:fortest>echo #コメント行です。 >> sample.txt
2:
3: c:fortest>echo 4-1,4-2,4-3,4-4,4-5 >> sample.txt
4:
5: c:fortest>type sample.txt
6: 1-1,1-2,1-3,1-4,1-5
7: 2-1,2-2,2-3,2-4,2-5
8: 3-1,3-2,3-3,3-4,3-5
9: #コメント行です。
10: 4-1,4-2,4-3,4-4,4-5
1: c:fortest>for /F "tokens=1,2,3,4,5 delims=, eol=#" %i IN (sample.txt) DO @echo %i %j %k %l %m
2: 1-1 1-2 1-3 1-4 1-5
3: 2-1 2-2 2-3 2-4 2-5
4: 3-1 3-2 3-3 3-4 3-5
5: 4-1 4-2 4-3 4-4 4-5
「eol=#」というオプションを追加することでコメント行をスキップすることが出来ました。
ファイル名に空白を含むファイルを扱いたい時
ここまでの知識でかなりの事が出来るのですが、このままだと「ファイル名に空白を含むものの処理」がうまくできなくて困ってしまいます。ちょっとやろうとしてみましょう。
1: c:fortest>echo hoge > "sample file.txt"
2:
3: c:fortest>dir
4: ドライブ C のボリューム ラベルがありません。
5: ボリューム シリアル番号は C0AF-1B4F です
6:
7: c:fortest のディレクトリ
8:
9: 2012/03/16 00:00 <DIR> .
10: 2012/03/16 00:00 <DIR> ..
11: 2012/03/15 22:05 <DIR> dir1
12: 2012/03/15 22:05 <DIR> dir10
13: 2012/03/15 22:05 <DIR> dir2
14: 2012/03/15 22:05 <DIR> dir3
15: 2012/03/15 22:05 <DIR> dir4
16: 2012/03/15 22:05 <DIR> dir5
17: 2012/03/15 22:05 <DIR> dir6
18: 2012/03/15 22:05 <DIR> dir7
19: 2012/03/15 22:05 <DIR> dir8
20: 2012/03/15 22:05 <DIR> dir9
21: 2012/03/16 00:00 7 sample file.txt
22: 2012/03/15 23:51 107 sample.txt
23: 2012/03/15 21:55 10 sample1.txt
24: 2012/03/15 21:55 11 sample10.txt
25: 2012/03/15 21:55 10 sample2.txt
26: 2012/03/15 21:55 10 sample3.txt
27: 2012/03/15 21:55 10 sample4.txt
28: 2012/03/15 21:55 10 sample5.txt
29: 2012/03/15 21:55 10 sample6.txt
30: 2012/03/15 21:55 10 sample7.txt
31: 2012/03/15 21:55 10 sample8.txt
32: 2012/03/15 21:55 10 sample9.txt
33: 12 個のファイル 215 バイト
34: 12 個のディレクトリ 142,817,693,696 バイトの空き領域
1: c:fortest>for /F %i IN (sample file.txt) DO @echo %i
2: ファイル sample が見つかりません。
3:
4: c:fortest>for /F %i IN ("sample file.txt") DO @echo %i
5: sample
6:
7: c:fortest>for /F %i IN ('sample file.txt') DO @echo %i
8: 'sample' は、内部コマンドまたは外部コマンド、
9: 操作可能なプログラムまたはバッチ ファイルとして認識されていません。
うーん。うまくいきません。こういう時のためにusebackqオプションがあります。このオプションを有効にすると”(ダブルクォート), ‘(シングルクォート), `(バッククォート)の扱いが変わります。
1: c:fortest>for /F "usebackq" %i IN ("sample file.txt") DO @echo %i
2: hoge
1: c:fortest>for /F "usebackq" %i IN ('ipconfig') DO @echo %i
2: ipconfig
3:
4: c:fortest>for /F "usebackq" %i IN (`ipconfig`) DO @echo %i
5: Windows
6: Wireless
7: 接続固有の
8: リンクローカル
9: IPv4
10: サブネット
11: デフォルト
12: イーサネット
13: メディアの状態.
14: 接続固有の
15: Tunnel
16: メディアの状態.
17: 接続固有の
18: Tunnel
19: メディアの状態.
20: 接続固有の
21: Tunnel
22: 接続固有の
23: IPv6
24: リンクローカル
25: デフォルト
26: Tunnel
27: メディアの状態.
28: 接続固有の
29: Tunnel
30: メディアの状態.
31: 接続固有の
このように、usebackqオプションを有効にすると、ダブルクォートは文字列ではなくてファイル名として扱われます。シングルクオートはコマンドではなく文字列になり、バッククォートがコマンドになります。
置換
変数を置換出来る機能も見ておきましょう。置換というか表現の変更といった方が近いかもしれません。
1: c:fortest>for /F %i IN (""test"") DO @echo %i
2: "test"
3:
4: c:fortest>for /F %i IN (""test"") DO @echo %~i
5: test
変数名の前に~をつけると引用句(“)が削除されます。
1: c:fortest>for %i IN (*.txt) DO @echo %i
2: sample file.txt
3: sample.txt
4: sample1.txt
5: sample10.txt
6: sample2.txt
7: sample3.txt
8: sample4.txt
9: sample5.txt
10: sample6.txt
11: sample7.txt
12: sample8.txt
13: sample9.txt
14:
15: c:fortest>for %i IN (*.txt) DO @echo %~fi
16: c:fortestsample file.txt
17: c:fortestsample.txt
18: c:fortestsample1.txt
19: c:fortestsample10.txt
20: c:fortestsample2.txt
21: c:fortestsample3.txt
22: c:fortestsample4.txt
23: c:fortestsample5.txt
24: c:fortestsample6.txt
25: c:fortestsample7.txt
26: c:fortestsample8.txt
27: c:fortestsample9.txt
変数名の前に~fをつけると絶対パスで表示します。
1: c:fortest>for %i IN (*.txt) DO @echo %~di
2: c:
3: c:
4: c:
5: c:
6: c:
7: c:
8: c:
9: c:
10: c:
11: c:
12: c:
13: c:
変数名の前に~dをつけるとドライブ名のみ表示します。
1: c:fortest>for %i IN (*.txt) DO @echo %~pi
2: fortest
3: fortest
4: fortest
5: fortest
6: fortest
7: fortest
8: fortest
9: fortest
10: fortest
11: fortest
12: fortest
13: fortest
変数名の前に~pをつけるとパス名のみ表示します。
1: c:fortest>for %i IN (*.txt) DO @echo %~ni
2: sample file
3: sample
4: sample1
5: sample10
6: sample2
7: sample3
8: sample4
9: sample5
10: sample6
11: sample7
12: sample8
13: sample9
変数名の前に~nをつけるとファイル名のみ表示します。
1: c:fortest>for %i IN (*.txt) DO @echo %~xi
2: .txt
3: .txt
4: .txt
5: .txt
6: .txt
7: .txt
8: .txt
9: .txt
10: .txt
11: .txt
12: .txt
13: .txt
変数名の前に~xをつけると拡張子のみ表示します。
1: c:fortest>for %i IN (*.txt) DO @echo %~si
2: c:fortestSAMPLE~1.TXT
3: c:fortestsample.txt
4: c:fortestsample1.txt
5: c:fortestsample10.txt
6: c:fortestsample2.txt
7: c:fortestsample3.txt
8: c:fortestsample4.txt
9: c:fortestsample5.txt
10: c:fortestsample6.txt
11: c:fortestsample7.txt
12: c:fortestsample8.txt
13: c:fortestsample9.txt
変数名の前に~sをつけると短い名前を表示します。
1: c:fortest>for %i IN (*.txt) DO @echo %~ai
2: --a------
3: --a------
4: --a------
5: --a------
6: --a------
7: --a------
8: --a------
9: --a------
10: --a------
11: --a------
12: --a------
13: --a------
変数名の前に~aをつけるとファイルの属性を表示します。
1: c:fortest>for %i IN (*.txt) DO @echo %~ti
2: 2012/03/16 00:00
3: 2012/03/15 23:51
4: 2012/03/15 21:55
5: 2012/03/15 21:55
6: 2012/03/15 21:55
7: 2012/03/15 21:55
8: 2012/03/15 21:55
9: 2012/03/15 21:55
10: 2012/03/15 21:55
11: 2012/03/15 21:55
12: 2012/03/15 21:55
13: 2012/03/15 21:55
変数名の前に~tをつけると日付/時刻を表示します。
1: c:fortest>for %i IN (*.txt) DO @echo %~zi
2: 7
3: 107
4: 10
5: 11
6: 10
7: 10
8: 10
9: 10
10: 10
11: 10
12: 10
13: 10
変数名の前に~zをつけるとファイルのサイズを表示します。
1: c:fortest>for %i IN (*.txt) DO echo %~$PATH:i
2:
3: c:fortest>echo
4: ECHO は <ON> です。
1: c:fortest>set Path=%Path%;c:fortest
2:
3: c:fortest>for %i IN (*.txt) DO echo %~$PATH:i
4:
5: c:fortest>echo c:fortestsample file.txt
6: c:fortestsample file.txt
上記の例はかなりわかりにくいですが、環境変数PATHの中に記述されているディレクトリを探し、そこに検索結果のファイルやディレクトリが合致すれば表示される、そうでなければ空になる、という動きをします。PATHに含まれているファイル、ディレクトリなのかどうかの判定に使えると思います。
バッチファイルに記述する場合の注意
ここまでforを見てきましたが全て「コマンドプロンプト上で実行する際の記述」であることに注意してください。ヘルプにも以下の記載があります。
バッチ プログラムで FOR コマンドを使用するときは、%変数の代わりに、%%変数を使用してください。
「コマンドプロンプトで動いたものをそのままバッチファイルに貼りつけてはいけない」ということです。
具体的には以下の例であれば
1: c:fortest>for %i IN (*.txt) DO @echo %i
2: sample file.txt
3: sample.txt
4: sample1.txt
5: sample10.txt
6: sample2.txt
7: sample3.txt
8: sample4.txt
9: sample5.txt
10: sample6.txt
11: sample7.txt
12: sample8.txt
13: sample9.txt
バッチファイルには以下の用に記述する必要があります。
1: for %%i IN (*.txt) DO @echo %%i
注意して下さい。
forの可能性
さて、ここまでforコマンドでできることを見てきました。「かなりの事ができる」ということがお分かりいただけたかと思います。「もういやだ」という人も多いかとは思いますが…。でも、バッチファイルを作るしか無いのであれば仕方が無いです……。
随分このエントリも長くなってしまいました。forを駆使した使える実例に関してはまた別のエントリで紹介したいと思います。
この記事の評価をお願いします!
Twitterもどうぞ
Twitterでは記事の更新情報も含め様々な事を呟いていますのでよろしければフォローいただければと思います。Facebookページへの「いいね!」もお願いします!
Facebookページで最新記事のお知らせと、外部サイトの紹介を行っています。是非「いいね!」をしていただき、最新の情報をFacebookで受け取ってください。
2 Responses
手元環境(Windows 7 Enterprise 64bit)では「再帰処理」の段落のコマンド、
for /L %i IN (1,1,10) DO echo sample%i > .dir%isample%i.txt
は、
for /L %i IN (1,1,10) DO echo sample%i > .\dir%i\sample%i.txt
としないと、フォルダは以下へのファイルは出来ませんでした。
さとっちさん。コメントありがとうございます。仰るとおりです。コマンドに間違いがありました。申し訳ないです。
そもそも、Wordpressの仕様との兼ね合いで「\」が全部消えてしまっていることに気が付きました。全体的に対処が必要なので対応しようと思います。
ありがとうございました。