バッチファイルでの環境変数の扱いは、一見シンプルに見えてハマりどころが多い分野です。「変数を更新したはずなのに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 内の変数は数値として計算されます。
@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ブロックまとめて展開」されます。そのため for や if のブロック内で変数を更新しても、%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
文字列長の取得
@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 以降に設定した変数は全て消去されます。サブルーチンや一時処理で変数が外部に漏れるのを防ぐのに有効です。
@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
endlocal の後は変数が消えてしまいますが、endlocal & set "RET=%RESULT%" のように同一行に書くことで、endlocal 前の値を展開してから外部スコープに返すテクニックがあります。setx で環境変数を永続化する
set で設定した変数はそのプロセスの終了とともに消えます。永続的な環境変数を設定するには 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が壊れます
@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" として基準パスを変数に入れておくのが定番です。よくある落とし穴と対処法
@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
@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% が空になるset VAR = value のように = の前後にスペースが入っている(変数名が VAR になる)、(2)setlocal の中で設定して endlocal 後に参照している、(3)FOR・IF ブロック内で設定して %VAR% で参照している(遅延展開が必要)、の3つです。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(システム全体・管理者権限必要)

