やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

batで標準出力の値を変数に入れる方法

表題の件について調べた。

console

答え

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

console

難解すぎる

FOR /F "tokens=1 usebackq delims=^:" %%i in (`echo %DESCRIPTION% ^| nkf -w`) DO @set DESCRIPTION_UTF8=%%i
  • echoで標準出力された値を、nkfUTF-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文怖すぎ。