batで標準出力の値を変数に入れる方法
表題の件について調べた。
答え
FOR /F "tokens=1 usebackq delims=^:" %%i in (`echo %DESCRIPTION% ^| nkf -w`) DO @set DESCRIPTION_UTF8=%%i
問題
よく、dirコマンドなど何かを標準出力するコマンドがある。 これらの値を変数に代入したいのだが、その方法がわからなかった。
ファイル出力なら簡単なのだが。
dir > out.txt
また、Unixのシェルスクリプト.sh
なら、おそらく以下のコードで済む。
UTF8=`echo %DESCRIPTION% | nkf -w`
SetStdOutValue.bat
@echo off
set DESCRIPTION=日本語の説明。
FOR /F "tokens=1 usebackq delims=^:" %%i in (`echo %DESCRIPTION% ^| nkf -w`) DO @set DESCRIPTION_UTF8=%%i
echo "Shift-JIS : %DESCRIPTION%"
echo "UTF-8 : %DESCRIPTION_UTF8%"
@echo on
難解すぎる
FOR /F "tokens=1 usebackq delims=^:" %%i in (`echo %DESCRIPTION% ^| nkf -w`) DO @set DESCRIPTION_UTF8=%%i
echo
で標準出力された値を、nkf
でUTF-8に変換する- それを変数
DESCRIPTION_UTF8
に代入する
解説…できない
いろいろ試してたまたま偶然動いただけ。
FOR /F "tokens=1 usebackq delims=^:" %%i in (`echo %DESCRIPTION% ^| nkf -w`) DO @set DESCRIPTION_UTF8=%%i
- バッチファイルなら
%%i
、コンソール入力なら%i
にする - パイプ
|
の数だけtokens
の値が増える(と思う) - パイプ
|
の前には必ず^
が必要(なぜかは知らん)
ヘルプが重い
for /?
でFOR文のヘルプを見てみた。以下、抜粋。
FOR %変数 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
この例は、現在の環境の環境変数名を列挙します。
意味不明…ヘルプミー
一行目から意味不明。「ファイル名セットは、1 つ以上のファイル名です」って何? 「ファイル名セット」って何?そんなのどこにある?
たぶんFOR %変数 IN (セット) DO コマンド [コマンドパラメータ]
にある(セット)
のこと。
でも、FOR /F ["オプション"] %変数 IN (
コマンド) DO コマンド [コマンド パラメータ]
の説明ではコマンド
になっている。何が何やらわからない。
ちょっとわかるコマンド例
コマンド例とその説明は何となくわかるかも?
コマンド例 | 説明 |
---|---|
FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do @echo %i %j %k |
myfile.txt の各行を解析します。セミコロンで始まる行を無視し、各行の 2 番目と 3 番目のトークンを for 本体に渡します。 |
FOR /F "usebackq delims==" %i IN ( set) DO @echo %i |
現在の環境の環境変数名を列挙します。 |
例をみてやっと「そんなことができるのか」と思える。
価値
有用なコマンドがあっても、変数に代入することすらできないのでは利用価値がさがる。
このイディオムはきっといつか役に立つはず。
書きたくない
さりとて、こんな難解なコードは書きたくない。
一見してこれが代入だとは思えない。混乱することは間違いない。
これを書くくらいなら、他のスクリプト言語を使いたい。
参考
http://d.hatena.ne.jp/iww/20110526/p1 http://oshiete.goo.ne.jp/qa/1483775.html
所感
FOR文恐怖症になってきた。batのFOR文怖すぎ。