rm /blog

IT系技術職のおっさんがIT技術とかライブとか日常とか雑多に語るブログです。* 本ブログに書かれている内容は個人の意見・感想であり、特定の組織に属するものではありません。/All opinions are my own.*

【Windows MS-DOS】コマンドプロンプトのfor文

個人的にいまいち使い勝手が悪いWindowsコマンドプロンプトのfor文のメモ。
Linuxに慣れすぎているせいだろうなんだろうな…)



 

 


■原始的な使い方

for %i in (a b c d e) do @echo %i

()内をスペース区切りで記述することで、区切られたそれぞれが各要素になる。
この結果は↓のようになる。

a
b
c
d
e

 



■ファイル、ディレクトリを表示

for %i in (a*) do @echo %i

"a*"で「(カレントディレクトリ上の)aから始まるファイル名のファイル」の一覧を取得したことになる。
「abcde.txt」「aaaaa.txt」「bcdefg.txt」が存在する場合は以下のようになる。

abcde.txt
aaaaa.txt


これはデフォルトではファイルしかひっかけない(ディレクトリ=フォルダは対象外になる)。
ディレクトリをひっかけたい場合は「/d」オープンを使う。

for /d %i in (a*) do @echo %i


ただ個人的な感想だが、こんなことはdirコマンドとfindstrコマンドの組み合わせで十分事足りるので、正直使う必要ないと思う。



■1から順に10まで標準出力

for /l %i in (1,1,10) do @echo %i

この書き方では(1,1,10)が左から順に「1から」「1ずつ増やして」「10まで」を意味する。

1
2
3
4
5
6
7
8
9
10


というわけなので、真ん中を0にすると無限ループになる。
Ctrl+Cでも抜けられない(というか抜けた直後に次のループの処理が始まる)のでタスクマネージャーから終了させるかtaskkillを別のところから流すしかなくなる。

for /l %i in (1,0,10) do @echo %i

 



■/fオプションのコマンドについて
()内をコマンドにできるオプション(それ以外にもあるみたいだが)。Linux的でなじみが深い。
なお、forのヘルプ(後述)には以下のような記述があるのだが、この通りにコマンドをいれても動いてくれない。

FOR /F ["オプション"] %変数 IN (`コマンド`) DO コマンド [コマンド パラメーター]

例えば「for /f %i in (`dir`) do @echo %i」と打つと、「ファイル `dir` が見つかりません。」と文句言われてコマンド失敗になる。

コマンドを()内に入れる場合は、`ではなく'を使う必要がある。

for /f %i in ('dir') do @echo %i

ただ、↑のやり方だとdirコマンド実行後に表示されるいろいろな列のうち、一番左側の列-すなわちファイルやディレクトリのタイムスタンプを表すYYYY/MM/dd形式の日付しか表示されない。
ファイル名とかディレクトリ名だけほしければ、中のコマンドを「'dir /b'」に変えたほうがいい。


というより、ヘルプの説明が前後していてわかりづらいが、
`をコマンドの評価として使う場合はオプションとして「usebackq」を入れておく必要があるようだ。

for /f "usebackq" %i in (`dir /b`) do @echo %i

こうすると`dir /b`部分がコマンド実行と解釈されて、変数%iに渡される内容もコマンドの実行結果になる。


ちなみにパイプ付のコマンドを実行する場合は、usebackqオプションに加えてコマンド内をさらにダブクォで囲ってあげればよいみたいである。

for /f "usebackq" %i in (`"dir /b /s | findstr ""memo"""`) do @echo %i

この例ではdir /bのパイプにfindstrをつなげている。
findstrは引数にダブクォでひっかける文字列を指定するので、そいつもさらにダブクォで囲ってやる。



■/fオプション delimsの扱いについて
/fオプション内では「delims」という値を使って区切りをつけられるが、
これはあくまでその行内をその値で区切ってという指示であって
forでループするそもそもの前提に改行してることというのが存在しているようである。
だから例えば環境変数の値を「;」(セミコロン)区切りで一律表示したいという要望として

for /f "usebackq delims=;"  %i in (`set Path`) do @echo %i

としたところで、環境変数「Path」ないし「PATHEXT」から始まり最初の「;」までの文字列が取得されるだけで終了となる。

> set Path

Path=C:\Windows\system32\;C:\hogehoge\;D:\hogehoge\;

↓

> for /f "usebackq delims=;"  %i in (`set Path`) do @echo %i

Path=C:\Windows\system32\

切ない………(まあこのくらいならjavaで組んだ方が簡単だし手っ取り早いだろう)

そういう意味だとdelimsはlinuxのcutコマンドと扱いが似ている。
こっちは変数名に限りがある(アルファベット小文字大文字合わせて52個が限界)等残念な制限があるが、基本的な考え方は同じようだ。
デフォルトでスペースとタブが設定されているらしいのでコマンドと併用する場合にあえて別の区切り文字を設定する必要性はなさそうだが、
例えば以下のような使い方ができるというわけだ。

> tasklist

イメージ名                     PID セッション名     セッション# メモリ使用量
========================= ======== ================ =========== ============
System Idle Process              0 Services                   0         24 K
…

↓

> for /f "usebackq tokens=1,2,5" %i in (`tasklist`) do @echo %i %j %k  
イメージ名 PID メモリ使用量
========================= ======== ============
System Idle Services
…

↑
(明示的にはdelimsを書いてないけど)スペース及びタブ区切りでtasklistコマンド結果の項目のうち1番目、2番目、5番目を取得する指示。
Linuxにおける(表示項目は違うものの)ps -efa | cut -d' ' -f1,2,5とやりたいことは同じ。

う~ん…微妙…
Linuxawk '{print $1}'のようにいい感じに切ってくれはしないらしい…
ちゃんと整形されたファイル相手なら問題なさそうだが…
そんなん気にするくらいならjavaで作っちゃったほうが手っ取り早いしな…



■バッチファイル上での扱い
バッチファイルでは変数名として用意されている部分の先頭に"%"をさらに余分でつけなくてはならない。
つまりコマンドプロンプト上で↓のように書く場合

for %i in (aaa bbb ccc) do @echo %i

これをバッチ(.batファイル)で同じ動作させる場合は

for %%i in (aaa bbb ccc) do @echo %%i

としてあげなければならない。(各オプションにて共通)



■ヘルプを表示

for /?

毎回打つの面倒だったら「for /? > for_help.txt」とかでリダイレクトしてファイルに落としてしまってもいいかもしれない。(というか俺はそうしている)