制御構文風コマンド for

前回はコマンドプロンプトやバッチファイルで利用できる制御構文風のコマンドとしてifを取り上げた。今回は同じく制御構文風の機能として利用できるforを紹介する。

コマンドプロンプトやバッチファイルのforはかなり理解しにくい機能なのだが、他人の書いたコードやコマンドを読み解くためには、ある程度おさえておく必要がある。

forまたはforeach、またはfor-inといった繰り返し処理向けの制御構文はプログラミング言語では必須に近いものになっている。コマンドプロンプトでもforを使うと似たようなことができるのだが、コマンドプロンプトやバッチファイルで使用できるforは、シェルスクリプトや現在主流のプログラミング言語で提供されている機能と比べるとかなり特殊な作りになっている。

次のようなシンタックスなのだが、forだけでさまざまな処理ができるように、その指定方法は少々複雑だ。

forコマンドの基本的な使い方

for [/d][/r [[ドライブ:]パス]][/l][/f ["パースキーワード"]] {%%|%}置換可能変数 in (セット) do コマンド [コマンドラインオプション]

基本的にコマンドプロンプトやバッチファイルでは制御構文の機能が極めて少なく、コマンドとして似たような機能を実現しようとしているため複雑なのだが、forの場合はfor自身が複数の異なる機能を持っていることで状況をさらに複雑にしている。

コマンドプロンプトやバッチファイルでforが使用できると結構複雑なことができるようになるのだが、お世辞にも読みやすいとはいえず、慣れないユーザにとっては書いてあることの意味がわからないことが多いだろう。

forでは次のパラメータを指定することができる。

パラメータ 内容
/d セット内のディレクトリに対して適用
/r ディレクトリを再帰的にたどって処理を実施
/l Start#およびEnd#の間でStep#づつ値を変更しながら処理を実施
/f 指定されたファイル内を解析
{%%|%}置換可能変数 必ず指定する必要がある置換可能変数名。コマンドプロンプトで使う場合には%を、バッチファイルで使う場合には%%を指定する。変数名は大文字および小文字が区別され、1文字で指定する必要がある
(セット) 必ず指定する必要があるファイル、ディレクトリ、テキスト文字列、範囲などの集合。括弧の指定も必須
コマンド 実行するコマンド
コマンドラインオプション 指定したコマンドで使用するコマンドラインオプション

パラメータからわかるように、コマンドプロンプトやバッチファイルのforでは変数を代入してインクリメントまたはデクリメントすることで繰り返し処理を行う使い方、ファイルやディレクトリの一覧から対象を持ってきて繰り返し処理を行う使い方、ファイルの一覧から対象を持ってきてファイルの中身を解析しながら繰り返し処理を行う使い方、コマンドの実行結果に対して繰り返し処理を行う使い方、などがある。forだけで複数のセマンティックの異なる使い方が可能で、これがforの使いにくさというか、覚えにくさにつながっている。

ファイルの中身を解析、またはコマンド結果を解析して繰り返し処理をする場合、次のキーワードを指定することで分析内容を指定できる。

パースキーワード 内容
eol=文字 行末文字を指定(1文字分だけ)
skip=数 ファイルの銭湯でスキップする行数を指定
delims=文字セット 区切り文字セットを指定。デフォルトは空白およびタブで、これを置き換える
tokens=<X,Y,M-N> 各行のどのトークンを繰り返しごとにforループに渡すかを指定。M-NはM番目からN番目という指定
usebackq コマンドとしてバッククォート文字列を実行するか、リテラル文字列としてシングルクオート文字列を使用するか、空白を含むファイル名を指定する場合などに使用

forでは最初に指定した置換可能変数に対して修飾を指定することができ、この修飾によって出力を変えられる。

基本的には次のようになる。

置換可能変数への修飾指定 内容
%~V 周囲のクォーテーションを削除
%~fV パス名を完全修飾パス名へ展開
%~dV パス名をドライブ名のみへ展開
%~pV パス名をパス名のみへ展開
%~nV パス名をファイル名のみへ展開
%~xV パス名を拡張子のみへ展開
%~sV パス名を短縮名のみへ展開
%~aV ファイル属性のみへ展開
%~tV 日付および時刻のみへ展開
%~zV ファイルサイズへ展開
%~$PATH:V 環境変数PATHのリストされているディレクトリから最初に見つかったディレクトリの完全修飾パス名へ展開

さらに修飾子は次のように組み合わせて使用することもできる。

置換可能変数への修飾指定の組み合わせ 内容
%~dpV ドライス名とパス名へ展開
%~nxV ファイル名と拡張子へ展開
%~fsV 短縮名でのフルパスへ展開
%~dp$PATH:V 環境変数PATHにリストされているディレクトリのうち、最初に見つかったディレクトリのドライブ名とパスへ展開
%~ftzaV Expands %Vをdirコマンドの出力のように展開

forは上記内容からでは書き方や動作を推測することが難しい。実行例を見ながら使い方を理解してもらえればと思う。

forの実行サンプル

forの使い方としてわかりやすいのは次のサンプルだろう。(*)でファイルがセットとして利用され、そのファイル名がechoで出力されている。このサンプルは置換可能変数に対して修飾子を指定して出力の内容を変更している。

forでファイルに対してコマンドを実行するサンプル

C:\Users\daich>for %f in (*) do echo %f

C:\Users\daich>echo aclfile.txt
aclfile.txt

C:\Users\daich>echo hoge.txt
hoge.txt

C:\Users\daich>for %f in (*) do echo %~ff

C:\Users\daich>echo C:\Users\daich\aclfile.txt
C:\Users\daich\aclfile.txt

C:\Users\daich>echo C:\Users\daich\hoge.txt
C:\Users\daich\hoge.txt

C:\Users\daich>for %f in (*) do echo %~df

C:\Users\daich>echo C:
C:

C:\Users\daich>echo C:
C:

C:\Users\daich>for %f in (*) do echo %~zf

C:\Users\daich>echo 350090
350090

C:\Users\daich>echo 5507
5507

C:\Users\daich>for %f in (*) do echo %~tf

C:\Users\daich>echo 2019/02/14 18:58
2019/02/14 18:58

C:\Users\daich>echo 2019/02/01 19:47
2019/02/01 19:47

C:\Users\daich>

もうひとつ理解しやすいのは、次のように指定した範囲で数を変えながら繰り返し処理を行う方法だろう。プログラミング言語としてはもっとも基本的なfor制御構文の使い方に近いやり方だ。

値を増やしながらコマンドの繰り返し処理

C:\Users\daich>for /l %i in (1,10,100) do echo %i

C:\Users\daich>echo 1
1

C:\Users\daich>echo 11
11

C:\Users\daich>echo 21
21

C:\Users\daich>echo 31
31

C:\Users\daich>echo 41
41

C:\Users\daich>echo 51
51

C:\Users\daich>echo 61
61

C:\Users\daich>echo 71
71

C:\Users\daich>echo 81
81

C:\Users\daich>echo 91
91

C:\Users\daich>

forでは逆にデクリメントしながらの処理も可能で、次のように使用すれば変数を減らしながら処理を進めることができる。

値を減らしながらのコマンドの繰り返し処理

C:\Users\daich>for /l %i in (1,-10,-100) do echo %i

C:\Users\daich>echo 1
1

C:\Users\daich>echo -9
-9

C:\Users\daich>echo -19
-19

C:\Users\daich>echo -29
-29

C:\Users\daich>echo -39
-39

C:\Users\daich>echo -49
-49

C:\Users\daich>echo -59
-59

C:\Users\daich>echo -69
-69

C:\Users\daich>echo -79
-79

C:\Users\daich>echo -89
-89

C:\Users\daich>echo -99
-99

C:\Users\daich>

次の実行サンプルは「set」というコマンドを実行したうえで、その出力結果を=区切りと分析して1列目の内容のみを出力するといったものだ。

setコマンドの出力を=区切りとして1列目を出力している

C:\Users\daich>for /f "usebackq delims==" %i in (`set`) do @echo %i
ALLUSERSPROFILE
APPDATA
CommonProgramFiles
CommonProgramFiles(x86)
CommonProgramW6432
COMPUTERNAME
ComSpec
DriverData
FPS_BROWSER_APP_PROFILE_STRING
FPS_BROWSER_USER_PROFILE_STRING
HOMEDRIVE
HOMEPATH
LOCALAPPDATA
LOGONSERVER
NUMBER_OF_PROCESSORS
OneDrive
OneDriveConsumer
OS
Path
PATHEXT
PROCESSOR_ARCHITECTURE
PROCESSOR_IDENTIFIER
PROCESSOR_LEVEL
PROCESSOR_REVISION
ProgramData
ProgramFiles
ProgramFiles(x86)
ProgramW6432
PROMPT
PSModulePath
PUBLIC
SESSIONNAME
SystemDrive
SystemRoot
TEMP
TMP
USERDOMAIN
USERDOMAIN_ROAMINGPROFILE
USERNAME
USERPROFILE
windir

C:\Users\daich>

2列目も出力するなら次のようになる。

上記コマンドで2列目も出力するサンプル

C:\Users\daich>for /f "usebackq tokens=1,2 delims==" %i in (`set`) do @echo %i → %j
ALLUSERSPROFILE → C:\ProgramData
APPDATA → C:\Users\daich\AppData\Roaming
CommonProgramFiles → C:\Program Files\Common Files
CommonProgramFiles(x86) → C:\Program Files (x86)\Common Files
CommonProgramW6432 → C:\Program Files\Common Files
COMPUTERNAME → PARAN10-ONGS
ComSpec → C:\WINDOWS\system32\cmd.exe
DriverData → C:\Windows\System32\Drivers\DriverData
FPS_BROWSER_APP_PROFILE_STRING → Internet Explorer
FPS_BROWSER_USER_PROFILE_STRING → Default
HOMEDRIVE → C:
HOMEPATH → \Users\daich
LOCALAPPDATA → C:\Users\daich\AppData\Local
LOGONSERVER → \\PARAN10-ONGS
NUMBER_OF_PROCESSORS → 2
OneDrive → C:\Users\daich\OneDrive
OneDriveConsumer → C:\Users\daich\OneDrive
OS → Windows_NT
Path → C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\;C:\Program Files\PowerShell\6\;C:\Users\daich\AppData\Local\Microsoft\WindowsApps;;C:\Users\daich\AppData\Local\Programs\Microsoft VS Code\bin
PATHEXT → .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
PROCESSOR_ARCHITECTURE → AMD64
PROCESSOR_IDENTIFIER → Intel64 Family 6 Model 70 Stepping 1, GenuineIntel
PROCESSOR_LEVEL → 6
PROCESSOR_REVISION → 4601
ProgramData → C:\ProgramData
ProgramFiles → C:\Program Files
ProgramFiles(x86) → C:\Program Files (x86)
ProgramW6432 → C:\Program Files
PROMPT → $P$G
PSModulePath → C:\Program Files\WindowsPowerShell\Modules;C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules
PUBLIC → C:\Users\Public
SESSIONNAME → Console
SystemDrive → C:
SystemRoot → C:\WINDOWS
TEMP → C:\Users\daich\AppData\Local\Temp
TMP → C:\Users\daich\AppData\Local\Temp
USERDOMAIN → PARAN10-ONGS
USERDOMAIN_ROAMINGPROFILE → PARAN10-ONGS
USERNAME → daich
USERPROFILE → C:\Users\daich
windir → C:\WINDOWS

C:\Users\daich>

似たようなことをファイルの中身に対して実施すると、次のようになる。

似たような処理をファイルに対して適用した場合

C:\Users\daich>for /f "eol=; tokens=1,2 delims= " %i in (COPYRIGHT.txt) do @echo %i, %j
#, $FreeBSD:
#, @(#)COPYRIGHT
The, compilation
following, terms:
Copyright, (c)
Redistribution, and
modification,, are
are, met:
1., Redistributions
notice,, this
2., Redistributions
notice,, this
documentation, and/or
THIS, SOFTWARE
ANY, EXPRESS
IMPLIED, WARRANTIES
ARE, DISCLAIMED.
FOR, ANY
DAMAGES, (INCLUDING,
OR, SERVICES;
HOWEVER, CAUSED
LIABILITY,, OR
OUT, OF
SUCH, DAMAGE.
The, 4.4BSD
terms:,
All, of
Releases, is
Copyright, 1979,
The, Regents
Redistribution, and
modification,, are
are, met:
1., Redistributions
notice,, this
2., Redistributions
notice,, this
documentation, and/or
3., All
must, display
This, product
California,, Berkeley
4., Neither
may, be
without, specific
THIS, SOFTWARE
ANY, EXPRESS
IMPLIED, WARRANTIES
ARE, DISCLAIMED.
FOR, ANY
DAMAGES, (INCLUDING,
OR, SERVICES;
HOWEVER, CAUSED
LIABILITY,, OR
OUT, OF
SUCH, DAMAGE.
The, Institute
National, Standards
given, us
In, the
of, the
Portions, of
the, second
Standard, Portable
(POSIX),, copyright
Engineers,, Inc.
and, the
document.,
In, the
of, the
This, material
Standards, Committee
Business, Equipment
Suite, 500,
Programming, Language
The, views
those, of
policies,, either
of, California.
NOTE:, The
source, has
ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change, and
included, below.
July, 22,
To, All
As, you
code, files
portions, of
that, such
contributors.,
Specifically,, the
", *
*, must
*, This
*, California,
Effective, immediately,
include, the
foregoing, paragraph
in, its
William, Hoskins
Director,, Office
University, of

C:\Users\daich>

このようにforを使うことでコマンドプロンプトやバッチファイルでも繰り返し処理を実施することができる。しかし、for自身が複数の異なる機能に対応していることから、forの動作は少々理解しにくいものになっている。

とはいえ、他人の用意したバッチファイルを読むとなると、やはりforの動作については理解しておく必要がある。サンプルを真似てコマンドを実行しながらforの使い方に慣れてもらえればと思う。

参考資料