2011年1月28日金曜日

バッチファイル。IF文やFOR文の中で複数コマンドを書く時の注意点 - Windowsのコマンドプロンプト(bat,cmd)

Windowsコマンド。制御文と環境変数
○Windows XP
IF 文や FOR 文の中で複数コマンドを実行したい時は、丸括弧(= 小括弧)を使うと便利です。
但し、次の時は、書き方を工夫する必要があります。
o IF 文や FOR 文の括弧内で環境変数の代入と参照を行いたい時
o 括弧内で、「"」に括られていない「)」を直接書きたい時


**** 問題となるパターン
o 括弧内で「"」に括られていない「)」を直接記述すると、文法エラーになります。
    + 全角の「)」は大丈夫です。
    + 変数の中に入っている「)」は大丈夫です。
o 次の2つの事を同時に行うと、コマンドプロンプトは予想外の値を返します。
    + 1つの丸括弧(= 小括弧)内で、環境変数に対して値を代入する
    + その括弧内において、代入した環境変数の値を参照する
o この時、参照した環境変数の値は、括弧内で代入する前の値となります。
    + 代入した値が有効になるのは、IF文(やFOR文)を抜けた直後からです。

--------
@echo off
set MIKU=ミクさん
if "%MIKU%"=="ミクさん" (
  set MIKU=ミク
  echo %MIKU%だよ
)
echo %MIKU%%MIKU%
--------

    + 予想は「ミクだよ」「ミクミク」
    + 結果は「ミクさんだよ」「ミクミク」


**** 問題となる原因
o コマンドプロンプトは、コマンドを1つずつ実行します。
o そして、IF文(やFOR文)は、括弧を含めて1つと解釈します。
o その結果、IF文(やFOR文)の中にある %環境変数% は、全て、IF文(やFOR文)を実行する直前の値で置き換わります。


**** 解決方法
o 3つの方法があります。
    + 参照と代入を別々の文に分けてしまう方法
    + CALL文を使う方法
    + 遅延環境変数を使う方法

** 参照と代入を別々の文に分けてしまう方法
--------
@echo off
set MIKU=ミクさん
if not "%MIKU%"=="ミクさん" goto XMIKU
set MIKU=ミク
echo %MIKU%だよ
:XMIKU
echo %MIKU%%MIKU%
--------

    + 結果は「ミクだよ」「ミクミク」
    + この書き方は、古いパソコンでも動きます。

** CALL文を使う方法
--------
@echo off
set MIKU=ミクさん
if "%MIKU%"=="ミクさん" call :x_call
echo %MIKU%%MIKU%
goto :eof

:x_call
set MIKU=ミク
echo %MIKU%だよ
goto :eof
--------

    + 結果は「ミクだよ」「ミクミク」
    + CALL先に限り、1文ずつ評価される状態になります。
    + 括弧の中から「echo %MIKU%だよ」の部分だけを CALL 文にする事も出来ます = 同じ結果になります。
    + この書き方は、古いパソコンで動くかどうかは分かりません。
    + CALL先をバッチファイルにして、goto :eofを使わなければ、古いパソコンでも動きます。

** 遅延環境変数を使う方法
o 遅延環境変数を使う場合、次の3点で書く方法がお手軽です。
    + setlocal をオプション付き
    + endlocal
    + 遅延評価したい環境変数は「%」の代わりに「!」
o 但し、環境変数の有効範囲が通常よりも狭くなっていますので、気を付けて使います。
    + setlocal の中で代入した環境変数の値は、endlocal やバッチファイルを抜けると元に戻ります。
--------
@echo off
setlocal enabledelayedexpansion
set MIKU=ミクさん
if "%MIKU%"=="ミクさん" (
  set MIKU=ミク
  echo !MIKU!だよ
)
echo %MIKU%%MIKU%
endlocal
--------

    + 結果は「ミクだよ」「ミクミク」
    + この書き方は、おそらく、Windows NT以前では動きません。
o この他に、コマンドプロンプト cmd.exe を遅延させるオプションを付けて呼び出し、その中で実行させる方法もあります。


**** 確認したバージョン
o Windows XP Service Pack 3


==
関連ページ:
    ▼Windowsコマンド。制御文と環境変数▼ABC順
    ▼Windowsコマンド一覧▼ABC順
    ▼コマンドプロンプト画面
    ▼制作メモ
    > %変数の使い方
    環境変数の使い方。置換と部分文字列
    環境変数の生存期間について
    +
    GOTO = 指定したラベルに移動する
    バッチファイルでif文の条件式を書く時の注意点
(2011年3月2日追加。FOR文対応と、CALLを使った回避方法)
(2011年1月30日追加。括弧内で「"」に括られていない「)」)