【bat】バッチファイルの環境変数完全ガイド|set・setx・遅延展開・文字列操作・スコープまで徹底解説

【bat】バッチファイルの環境変数完全ガイド|set・setx・遅延展開・文字列操作・スコープまで徹底解説 bat

バッチファイルでの環境変数の扱いは、一見シンプルに見えてハマりどころが多い分野です。「変数を更新したはずなのにFORループ内で反映されない」「setで設定したはずなのに値が空になる」—— こうした問題の多くは環境変数の仕組みを理解することで解決できます。

本記事では set / setx の基本から、数値演算・遅延展開・文字列操作・スコープ管理まで、実務でよく使うパターンをコード付きで体系的に解説します。

この記事で学べること

  • set で変数を設定・参照する基本構文と落とし穴
  • set /a による数値演算(四則演算・剰余・ビット演算)
  • set /p によるユーザー入力の受け取り方
  • 遅延展開(!変数!)の仕組みと使いどころ
  • 文字列の切り出し・置換・長さ取得
  • setlocal / endlocal によるスコープ管理
  • setx で環境変数を永続化する方法
  • Windowsの組み込み環境変数一覧
スポンサーリンク

基本:set で変数を設定・参照する

バッチファイルで変数を使うには set コマンドで設定し、%変数名% で参照します。

変数の設定と参照
@echo off
setlocal

:: 変数を設定(ダブルクォートで囲む形式を推奨)
set "NAME=山田太郎"
set "AGE=30"
set "DIR=C:\work"

:: 変数を参照
echo 名前: %NAME%
echo 年齢: %AGE%
echo 作業フォルダ: %DIR%

endlocal

必ずダブルクォートで囲む理由

set NAME=山田太郎 のようにクォートなしで書くと、行末のスペースが値に含まれてしまいます。set "NAME=山田太郎" とクォートで囲むとスペースが含まれず安全です。また = の前後にスペースを入れると変数名や値にスペースが含まれるため、必ず set "VAR=value" の形式を使いましょう。

変数の削除・空チェック

変数の削除と未定義チェック
@echo off
setlocal

set "MYVAR=hello"
echo 設定後: [%MYVAR%]

:: 変数を削除(右辺を空にする)
set "MYVAR="
echo 削除後: [%MYVAR%]

:: 未定義・空チェック
if not defined MYVAR (
    echo MYVAR は未定義または空です。
)

:: defined は「=」以降が空でも defined と判定されない
set "FLAG="
if defined FLAG (
    echo FLAG は定義済み
) else (
    echo FLAG は未定義
)

endlocal
if defined vs if "%VAR%"==""if defined VAR は変数が存在し空でない場合に true になります。set "VAR=" で空にした後は defined が false になります。一方 if "%VAR%"=="" は変数が未定義か空文字の場合に一致します。動作が異なるため目的に応じて使い分けてください。

set /a で数値演算を行う

set /a を使うと四則演算やビット演算ができます。文字列として扱われる通常の変数と異なり、set /a 内の変数は数値として計算されます。

set /a による数値演算
@echo off
setlocal

:: 四則演算
set /a "RESULT = 10 + 3"
echo 10 + 3 = %RESULT%

set /a "RESULT = 10 - 3"
echo 10 - 3 = %RESULT%

set /a "RESULT = 10 * 3"
echo 10 * 3 = %RESULT%

set /a "RESULT = 10 / 3"
echo 10 / 3 = %RESULT%   ← 整数除算(小数点切り捨て)= 3

:: 剰余(余り)
set /a "MOD = 10 %% 3"
echo 10 mod 3 = %MOD%    ← 1

:: インクリメント(カウンタ)
set /a "COUNT = 0"
set /a "COUNT += 1"
set /a "COUNT += 1"
echo COUNT = %COUNT%     ← 2

:: 累乗はできない(代わりに掛け算を繰り返す)
set /a "SQUARE = 5 * 5"
echo 5^2 = %SQUARE%

endlocal

set /a の注意点

  • 整数演算のみ(小数点は扱えない)
  • バッチ内で %% と書く必要がある(コマンドプロンプト直打ちなら % 1つでよい)
  • 結果は符号付き32bit整数(-2,147,483,648 〜 2,147,483,647)
  • 変数を set /a 内で使うときは % なしでも参照できる(例:set /a X = COUNT + 1

set /p でユーザー入力を受け取る

set /p を使うと対話形式で値を入力させられます。プロンプトメッセージを表示してユーザーに入力を求め、その値を変数に格納します。

ユーザー入力の受け取り
@echo off
setlocal

:: ユーザーに入力を求める
set /p "FOLDER=対象フォルダのパスを入力してください: "

:: 入力チェック
if not defined FOLDER (
    echo [ERROR] 入力がありませんでした。
    exit /b 1
)

if not exist "%FOLDER%" (
    echo [ERROR] フォルダが存在しません: %FOLDER%
    exit /b 1
)

echo [OK] %FOLDER% を処理します...
endlocal

入力バリデーションや Y/N 確認ダイアログの詳細についてはバッチファイルでユーザー入力を受け取る方法で解説しています。

遅延展開(!変数!)—— FOR ループ内で変数を更新する

バッチのコマンドは「実行前に1ブロックまとめて展開」されます。そのため forif のブロック内で変数を更新しても、%VAR% では更新前の値しか取得できません。これを解決するのが setlocal enabledelayedexpansion!変数名! です。

遅延展開なし(問題のあるコード)
@echo off
setlocal

set "COUNT=0"
for %%F in (*.txt) do (
    set /a "COUNT += 1"
    echo %%F を処理中... COUNT=%COUNT%    ← 常に 0 と表示される
)
echo 合計: %COUNT%    ← 最終的に 0 のまま

endlocal
遅延展開あり(正しいコード)
@echo off
setlocal enabledelayedexpansion

set "COUNT=0"
for %%F in (*.txt) do (
    set /a "COUNT += 1"
    echo %%F を処理中... COUNT=!COUNT!    ← 正しく更新される
)
echo 合計: !COUNT!

endlocal

遅延展開の仕組みや ! の特殊文字としての扱いについてはsetlocal enabledelayedexpansion 完全ガイドで詳しく解説しています。

文字列操作:切り出し・置換・長さ取得

部分文字列の切り出し(:~開始,長さ)

文字列の切り出し
@echo off
setlocal

set "STR=HelloWorld"

:: %VAR:~開始位置,文字数%
echo 先頭から5文字: %STR:~0,5%       ← Hello
echo 6文字目以降:   %STR:~5%         ← World
echo 末尾3文字:     %STR:~-3%        ← rld
echo 末尾から3文字目までの5文字: %STR:~-8,5%  ← lloWo

:: 日付から年月日を取り出す例(ロケール依存に注意)
echo 年: %DATE:~0,4%
echo 月: %DATE:~5,2%
echo 日: %DATE:~8,2%

endlocal

文字列の置換(:検索=置換)

文字列の置換
@echo off
setlocal

set "PATH_STR=C:\work\data\file.txt"

:: スラッシュに変換(\→/)
set "UNIX_PATH=%PATH_STR:\=/%"
echo Unix形式: %UNIX_PATH%    ← C:/work/data/file.txt

:: 特定文字を削除(置換先を空にする)
set "CLEAN=%PATH_STR:.txt=%"
echo 拡張子削除: %CLEAN%      ← C:\work\data\file

:: スペースを削除
set "STR_WITH_SPACE= hello world "
set "NO_SPACE=%STR_WITH_SPACE: =%"
echo スペース削除: [%NO_SPACE%]  ← [helloworld]

endlocal

文字列長の取得

文字列長を取得する(PowerShell利用)
@echo off
setlocal

set "STR=HelloWorld"

:: PowerShellで文字列長を取得
for /f %%L in ('powershell -command "\"%STR%\".Length"') do (
    set "LEN=%%L"
)
echo 文字列長: %LEN%    ← 10

endlocal

文字列の切り出しや操作の詳細はバッチファイルで文字列を切り出す方法、文字列の結合はバッチファイルで文字列を結合する方法を参照してください。

setlocal / endlocal でスコープを管理する

setlocal を使うと変数のスコープをそのブロック内に閉じ込められます。endlocal を実行すると setlocal 以降に設定した変数は全て消去されます。サブルーチンや一時処理で変数が外部に漏れるのを防ぐのに有効です。

setlocal によるスコープ管理
@echo off
set "GLOBAL_VAR=グローバル"

call :sub_routine
echo 呼び出し後: %GLOBAL_VAR%    ← グローバル(変わっていない)
echo サブ変数:   %LOCAL_VAR%     ← (空。endlocalで消えた)

pause
goto :EOF

:sub_routine
setlocal
set "LOCAL_VAR=ローカル"
set "GLOBAL_VAR=書き換え試み"
echo サブ内: %GLOBAL_VAR%    ← 書き換え試み(setlocal内では有効)
endlocal
goto :EOF
setlocal から値を返す方法endlocal の後は変数が消えてしまいますが、endlocal & set "RET=%RESULT%" のように同一行に書くことで、endlocal 前の値を展開してから外部スコープに返すテクニックがあります。

setx で環境変数を永続化する

set で設定した変数はそのプロセスの終了とともに消えます。永続的な環境変数を設定するには setx コマンドを使います。

setx による永続的な環境変数の設定
@echo off

:: 現在のユーザーに環境変数を設定(管理者権限不要)
setx MYAPP_HOME "C:\tools\myapp"

:: システム全体(全ユーザー)に設定(管理者権限が必要)
setx SHARED_DIR "\\server\shared" /M

echo 設定完了。新しいコマンドプロンプトから有効になります。
コマンド スコープ 有効になるタイミング 管理者権限
set 現在のプロセスのみ 即時 不要
setx(/M なし) 現在のユーザー(永続) 次回起動のコマンドプロンプトから 不要
setx ... /M システム全体(永続) 次回起動のコマンドプロンプトから 必要

setx の注意点

  • 設定した変数は現在開いているコマンドプロンプトには反映されません(新しいウィンドウから有効)
  • setx は1024文字を超える値を切り捨てます(PATH の操作に注意)
  • PATH を setx で追記する場合は必ず既存の値を読み取ってから追記してください。上書きするとPATHが壊れます
PATH に安全に追記する方法
@echo off
:: 現在のユーザーPATHに新しいパスを追記
:: (/M なしでユーザー環境変数のみ変更)
for /f "usebackq skip=2 tokens=3*" %%A in (
    `reg query "HKCU\Environment" /v Path 2^>nul`
) do set "CURRENT_PATH=%%A %%B"

setx PATH "%CURRENT_PATH%;C:\tools\newapp"
echo PATH を更新しました。

Windowsの組み込み環境変数一覧

Windowsにはあらかじめ定義された環境変数が多数あります。よく使うものをまとめました。

変数 内容
%USERNAME% 現在のユーザー名 yamada
%USERPROFILE% ユーザープロファイルのパス C:\Users\yamada
%APPDATA% ローミングアプリデータ C:\Users\yamada\AppData\Roaming
%LOCALAPPDATA% ローカルアプリデータ C:\Users\yamada\AppData\Local
%TEMP% / %TMP% 一時ファイルの保存場所 C:\Users\yamada\AppData\Local\Temp
%COMPUTERNAME% コンピューター名 PC-YAMADA
%DATE% 現在の日付(ロケール依存) 2026/03/21
%TIME% 現在の時刻 14:35:22.12
%CD% 現在のディレクトリ C:\work
%PATH% 実行ファイルの検索パス C:\Windows\system32;…
%WINDIR% / %SYSTEMROOT% Windowsのインストールフォルダ C:\Windows
%SYSTEMDRIVE% システムドライブ C:
%ERRORLEVEL% 直前のコマンドの終了コード 0(成功)
%RANDOM% 0〜32767のランダム数値 14523
%~dp0 実行中スクリプトのあるフォルダ C:\scripts\
%~n0 スクリプトのファイル名(拡張子なし) deploy
%~dp0 はスクリプト基準パスに必須バッチを別のフォルダから呼び出した場合、%CD% は呼び出し元のディレクトリになります。スクリプト自身のフォルダを確実に取得するには %~dp0 を使ってください。set "BASE=%~dp0" として基準パスを変数に入れておくのが定番です。

よくある落とし穴と対処法

落とし穴①:FOR ループ内の %% の扱い
@echo off
:: バッチファイル内では FOR の変数を %% で書く(% 1つではない)
for %%F in (*.txt) do (
    echo %%F
)

:: コマンドプロンプトで直接打つ場合は % 1つ
:: for %F in (*.txt) do echo %F
落とし穴②:変数内の % や ^ の扱い
@echo off
setlocal

:: % を値に含める場合は %% と書く
set "PERCENT=50%%"
echo %PERCENT%    ← 50%

:: ^ (キャレット)はエスケープ文字。値に含める場合は ^^
set "CARET=a^^b"
echo %CARET%      ← a^b

:: ! を値に含める場合(遅延展開有効時)は ^! と書く
setlocal enabledelayedexpansion
set "EXCL=hello^!world"
echo !EXCL!       ← hello!world

endlocal
endlocal
落とし穴③:数値比較には if EQU を使う
@echo off
setlocal

set "COUNT=10"

:: NG: 文字列比較になる("10" > "9" は false になる場合がある)
if "%COUNT%" GTR "9" echo 10 は 9 より大きい ← 期待通りにならない場合がある

:: OK: 数値比較には if /i を外し、数値としての EQU/GTR/LSS を使う
if %COUNT% GTR 9 echo 10 は 9 より大きい ← 正しく比較される

endlocal

数値比較の詳細はバッチファイルで数値を比較する方法、文字列比較はバッチファイルで文字列を比較する方法を参照してください。

よくある質問(FAQ)

変数を設定したのに echo %VAR% が空になる
主な原因は(1)set VAR = value のように = の前後にスペースが入っている(変数名が VAR になる)、(2)setlocal の中で設定して endlocal 後に参照している、(3)FOR・IF ブロック内で設定して %VAR% で参照している(遅延展開が必要)、の3つです。
FOR ループ内でカウンタを更新できない
setlocal enabledelayedexpansion を使い、ループ内では %COUNT% ではなく !COUNT! で参照してください。%COUNT% は行が解析される時点(ループ開始前)に展開されてしまうため、ループ内で更新しても反映されません。
setx で設定したのに反映されない
setx で設定した変数は現在開いているコマンドプロンプトには反映されません。設定後に新しくコマンドプロンプトを開いてから確認してください。
変数の値に特殊文字(パイプ |&)を含めたい
set "VAR=a|b" のようにダブルクォートで囲めば |& を値に含められます。ただし "(ダブルクォート)自体を含めるのはバッチでは困難です。PowerShellを呼び出して処理する方が確実です。
set /a で小数点の計算はできるか?
できません。set /a は整数演算のみです。小数点の計算が必要な場合は powershell -command "(10/3)" などでPowerShellを呼び出すのが最もシンプルです。
%~dp0%CD% の違いは何か?
%~dp0 はスクリプトファイル自身があるフォルダのパスです(末尾に \ あり)。%CD% は現在の作業ディレクトリで、バッチをどこから呼び出したかによって変わります。スクリプトと同じ場所のファイルを参照するには必ず %~dp0 を使ってください。

まとめ

バッチファイルの環境変数操作を整理します。

  • 基本set "VAR=値" で設定、%VAR% で参照(クォートで囲む形式を推奨)
  • 数値演算set /a "RESULT = 式"(整数のみ)
  • ユーザー入力set /p "VAR=プロンプト"
  • 遅延展開setlocal enabledelayedexpansion + !VAR!(ループ内の更新に必須)
  • 文字列操作%VAR:~開始,長さ%(切り出し)、%VAR:検索=置換%(置換)
  • スコープsetlocal / endlocal で変数の影響範囲を制御
  • 永続化setx(ユーザー)、setx /M(システム全体・管理者権限必要)