【bat】バッチファイルで引数を渡す方法完全ガイド|%1〜%9・%~修飾子・shift・名前付き引数・バリデーション・実践パターンまで

バッチファイルに引数を渡すことで、同じスクリプトを様々な入力に対応させられます。ファイルパスを渡す、動作モードを切り替える、複数ファイルをまとめて処理するなど、引数の活用で自動化の幅が大きく広がります。

この記事では %1〜%9の基本から、%~修飾子・shift・名前付き引数・フラグ引数・バリデーション・デフォルト値設定 まで、体系的に解説します。

この記事でわかること

  • %0〜%9・%* による引数の受け取り方
  • %~ 修飾子によるパス・ファイル名の展開
  • shift で引数をスライドして可変長引数を処理する方法
  • 引数のバリデーション(存在確認・型チェック)
  • デフォルト値の設定パターン
  • 名前付き引数(/key:value)とフラグ引数(/flag)の実装
  • サブルーチンへの引数渡しと戻り値の受け取り
  • 落とし穴5選・実践例3本・FAQ6問
スポンサーリンク

1. 引数の基本:%0〜%9 と %*

バッチファイルを script.bat arg1 arg2 arg3 のように実行すると、各引数が %1%2%3 に格納されます。%0 はバッチファイル自身のパスです。

@echo off
setlocal

echo バッチ自身のパス: %0
echo 第1引数: %1
echo 第2引数: %2
echo 第3引数: %3
echo すべての引数: %*

実行例:

script.bat apple banana cherry

出力:
バッチ自身のパス: script.bat
第1引数: apple
第2引数: banana
第3引数: cherry
すべての引数: apple banana cherry
変数 内容
%0 バッチファイル自身のパス(実行方法により変わる)
%1〜%9 1番目〜9番目の引数
%* 渡されたすべての引数をまとめた文字列

2. %~ 修飾子:引数からパスやファイル名を展開する

引数に渡されたファイルパスから、ドライブ名・フォルダ・ファイル名・拡張子などを個別に取り出せます。ファイルパス操作の詳細は バッチファイルのパスやファイル名を取得する方法も参照してください。

主要な %~ 修飾子一覧

修飾子 展開される内容 例(引数: C:\work\data.csv)
%~1 引用符を除いた引数の値 C:\work\data.csv
%~f1 絶対パスに展開 C:\work\data.csv
%~d1 ドライブ文字のみ C:
%~p1 パス部分のみ(末尾 \ 付き) \work\
%~n1 拡張子なしのファイル名 data
%~x1 拡張子のみ(ドット付き) .csv
%~dp1 ドライブ + パス C:\work\
%~nx1 ファイル名 + 拡張子 data.csv
%~z1 ファイルサイズ(バイト) 1024
%~t1 ファイルの更新日時 2025/01/01 12:00

実用例:渡されたファイルをバックアップ

@echo off
setlocal

:: 引数チェック
if "%~1"=="" (
    echo 使い方: %~nx0 ファイルパス
    exit /b 1
)

:: パスを分解
set "SRC=%~f1"
set "DIR=%~dp1"
set "NAME=%~n1"
set "EXT=%~x1"

:: バックアップ先ファイル名を生成(元のファイル名_backup.拡張子)
set "BACKUP=%DIR%%NAME%_backup%EXT%"

if not exist "%SRC%" (
    echo [ERROR] ファイルが見つかりません: %SRC%
    exit /b 1
)

copy "%SRC%" "%BACKUP%" >nul
echo [OK] バックアップ作成: %BACKUP%

3. shift でスライドして可変長引数を処理する

shift を使うと引数を1つ左にずらせます。%2%1 に、%3%2 になります。これで9個以上の引数や、個数が不定の引数をループ処理できます。

shift の基本動作

@echo off
setlocal

echo 実行前: %1 %2 %3
shift
echo shift後: %1 %2 %3

:: 実行: script.bat A B C D
:: 実行前: A B C
:: shift後: B C D

すべての引数をループ処理する

@echo off
setlocal enabledelayedexpansion

set "CNT=0"

:loop
if "%~1"=="" goto :done

set /a CNT+=1
echo 引数 !CNT!: %1
shift
goto :loop

:done
echo 引数の合計: %CNT% 個

実行例: script.bat file1.txt file2.txt file3.txt

先頭引数を変数に保存してから残りをシフトループで処理する

@echo off
setlocal enabledelayedexpansion

:: 第1引数(コマンド名)を変数に保存してから shift
:: → 以降のループで %1 は元の %2・%3... を順に指すようになる
set "CMD=%~1"
shift

echo コマンド: %CMD%
:file_loop
if "%~1"=="" goto :file_done
echo 対象ファイル: %1
shift
goto :file_loop
:file_done

:: 実行: script.bat backup a.txt b.txt c.txt
:: → コマンド: backup, 対象: a.txt b.txt c.txt

4. 引数のバリデーション

引数の有無・内容・ファイル存在などを実行前に確認することで、意図しない処理を防げます。

4-1. 必須引数の確認

@echo off
setlocal

:: 引数が指定されているか確認
if "%~1"=="" (
    echo [ERROR] 引数が不足しています
    echo 使い方: %~nx0 入力ファイル 出力フォルダ
    echo 例:     %~nx0 C:\data\input.csv C:\output
    exit /b 1
)
if "%~2"=="" (
    echo [ERROR] 出力フォルダを指定してください
    exit /b 1
)

set "INPUT=%~1"
set "OUTDIR=%~2"

:: ファイルの存在確認
if not exist "%INPUT%" (
    echo [ERROR] 入力ファイルが見つかりません: %INPUT%
    exit /b 1
)

echo [OK] 入力: %INPUT% / 出力先: %OUTDIR%

4-2. 引数の型チェック(数値かどうか)

@echo off
setlocal

:: 数値チェック: set /a で変換を試み、エラーなら非数値
set "NUM=%~1"
set /a TEST=%NUM% 2>nul
if "%TEST%"=="%NUM%" (
    echo %NUM% は数値です
) else (
    echo [ERROR] %NUM% は数値ではありません
    exit /b 1
)

4-3. 引数の選択肢チェック

@echo off
setlocal

set "ENV=%~1"

:: 許可された値かを確認
if /i "%ENV%"=="production" goto :ok
if /i "%ENV%"=="staging"    goto :ok
if /i "%ENV%"=="dev"        goto :ok

echo [ERROR] 環境名が不正: %ENV%
echo         production / staging / dev のいずれかを指定してください
exit /b 1

:ok
echo 環境: %ENV%

5. デフォルト値の設定

引数が省略された場合にデフォルト値を使うパターンです。

@echo off
setlocal

:: 引数が空ならデフォルト値を設定
if "%~1"=="" (
    set "INPUT=C:\work\default_input.csv"
) else (
    set "INPUT=%~1"
)

if "%~2"=="" (
    set "OUTPUT=C:\work\output"
) else (
    set "OUTPUT=%~2"
)

echo 入力: %INPUT%
echo 出力: %OUTPUT%

より簡潔に書くには if not defined を利用する方法もあります。

@echo off
setlocal

:: 引数を変数にセット(未指定なら空のまま)
set "INPUT=%~1"
set "MODE=%~2"

:: 空ならデフォルト値を上書き
if not defined INPUT set "INPUT=C:\work\default.csv"
if not defined MODE  set "MODE=normal"

echo 入力: %INPUT%, モード: %MODE%

6. 名前付き引数(/key:value 形式)

script.bat /input:data.csv /mode:fast のようなLinuxコマンドライン風の引数スタイルです。引数の順序を気にしなくて済むため、オプションが多いスクリプトに向いています。

@echo off
setlocal enabledelayedexpansion

:: デフォルト値
set "INPUT="
set "OUTPUT=C:\work\out"
set "MODE=normal"

:: 引数を順番にパース
:parse
if "%~1"=="" goto :end_parse

set "ARG=%~1"

:: /input:値 の形式で処理
if /i "!ARG:~0,7!"=="/input:" (
    set "INPUT=!ARG:~7!"
    shift & goto :parse
)
if /i "!ARG:~0,8!"=="/output:" (
    set "OUTPUT=!ARG:~8!"
    shift & goto :parse
)
if /i "!ARG:~0,6!"=="/mode:" (
    set "MODE=!ARG:~6!"
    shift & goto :parse
)

echo [WARN] 不明な引数: %1
shift
goto :parse

:end_parse

:: 必須引数チェック
if not defined INPUT (
    echo [ERROR] /input:ファイルパス を指定してください
    exit /b 1
)

echo 入力: %INPUT%
echo 出力: %OUTPUT%
echo モード: %MODE%

:: 実行例: script.bat /input:C:\data.csv /mode:fast /output:C:\out

7. フラグ引数(/flag スイッチ形式)

script.bat /verbose /dryrun のようなフラグ形式の引数パターンです。

@echo off
setlocal enabledelayedexpansion

:: フラグの初期値
set "VERBOSE=0"
set "DRYRUN=0"
set "FORCE=0"

:parse
if "%~1"=="" goto :end_parse

if /i "%~1"=="/verbose" ( set "VERBOSE=1" & shift & goto :parse )
if /i "%~1"=="/dryrun"  ( set "DRYRUN=1"  & shift & goto :parse )
if /i "%~1"=="/force"   ( set "FORCE=1"   & shift & goto :parse )
if /i "%~1"=="/help"    ( goto :usage )

:: 位置引数(フラグ以外)を受け取る場合
set "TARGET=%~1"
shift
goto :parse

:end_parse

if "%VERBOSE%"=="1" echo [詳細モード有効]
if "%DRYRUN%"=="1"  echo [ドライラン: 実際には変更しません]
if "%FORCE%"=="1"   echo [強制モード有効]
echo 対象: %TARGET%
exit /b 0

:usage
echo 使い方: %~nx0 [オプション] 対象パス
echo オプション:
echo   /verbose   詳細ログを表示
echo   /dryrun    実行確認のみ(変更なし)
echo   /force     確認なし強制実行
echo   /help      このヘルプを表示
exit /b 0

:: 実行例: script.bat /verbose /dryrun C:\work

8. サブルーチンへの引数渡し

CALL でサブルーチンを呼び出す際も同様に引数を渡せます。サブルーチン完全ガイドも参照してください。

@echo off
setlocal

:: サブルーチンを引数付きで呼び出す
call :greet "山田太郎" "おはよう"
call :greet "鈴木花子" "こんにちは"
exit /b 0

:greet
:: %~1: 名前(引用符なしで受け取る)
:: %~2: 挨拶
echo %~2、%~1さん!
exit /b 0

サブルーチンからの戻り値(変数経由)

@echo off
setlocal enabledelayedexpansion

call :calc_sum 10 20
echo 合計: %RESULT%
exit /b 0

:calc_sum
set /a RESULT=%~1 + %~2
exit /b 0

9. 落とし穴5選と対策

落とし穴1:スペースを含む引数を引用符で囲まないとエラー

:: NG: パスにスペースがあると %1 が途中で切れる
script.bat C:\my folder\data.csv
:: → %1 = C:\my, %2 = folder\data.csv(2つに分かれる)

:: OK: " " で囲んでから渡す
script.bat "C:\my folder\data.csv"
:: → %1 = "C:\my folder\data.csv"

:: バッチ内では %~1 で引用符を取り除いて使う
set "FILE=%~1"
if exist "%FILE%" echo ファイルが見つかりました

パスにスペースが含まれているとエラーになるときの解決策も参照してください。

落とし穴2:%~1 と %1 の使い分けを間違える

:: %1 → 引用符を含んだままの値: "C:\work\file.csv"
:: %~1 → 引用符を除いた値:    C:\work\file.csv

:: NG: %1 をそのまま if exist に渡すと ""-quoted になり二重引用符に
if exist %1 echo OK

:: OK: %~1 で引用符を除去し、if exist の中で改めて " で囲む
if exist "%~1" echo OK

落とし穴3:shift した後は %0 の内容が変わる

@echo off
echo shift 前 %%0: %0
shift
echo shift 後 %%0: %0
:: → shift 後、%0 は元の %1 の内容になる!
:: → %~nx0(スクリプト名取得)は shift 前に変数に保存しておく

:: OK: スクリプト名を事前に保存
set "SCRIPT_NAME=%~nx0"
shift
echo スクリプト名: %SCRIPT_NAME%

落とし穴4:%* は shift しても変わらない

@echo off
echo shift前 %%*: %*
shift
echo shift後 %%*: %*
:: → %* は shift の影響を受けず、常に最初の引数リストを保持する
:: 全引数を shift しながら処理する場合は %1 ループを使う

この挙動は 変数展開が思った通りに動かない原因と修正方法でも詳しく解説しています。

落とし穴5:引数に特殊文字(& ^ | など)が含まれると誤動作

:: NG: 特殊文字を含む引数をそのまま処理するとコマンドが壊れる
script.bat "data&info.csv"
:: → & がコマンド区切りとして解釈される場合がある

:: OK: バッチ内では変数経由でアクセス(" で囲んで保護)
set "FILE=%~1"
echo ファイル: "%FILE%"

:: OK: 呼び出し元で ^ でエスケープする
script.bat "data^&info.csv"

10. 実践例3本

実践例1:汎用ファイル変換スクリプト(入出力パス引数)

@echo off
setlocal

:: ===================================================
:: convert.bat 入力ファイル [出力フォルダ] [/verbose]
:: ===================================================

set "SCRIPT=%~nx0"
set "VERBOSE=0"
set "INPUT="
set "OUTPUT="

:parse
if "%~1"=="" goto :end_parse
if /i "%~1"=="/verbose" ( set "VERBOSE=1" & shift & goto :parse )
if not defined INPUT  ( set "INPUT=%~1"  & shift & goto :parse )
if not defined OUTPUT ( set "OUTPUT=%~1" & shift & goto :parse )
shift
goto :parse
:end_parse

:: 必須チェック
if not defined INPUT (
    echo 使い方: %SCRIPT% 入力ファイル [出力フォルダ] [/verbose]
    exit /b 1
)
if not exist "%INPUT%" (
    echo [ERROR] 入力ファイルが見つかりません: %INPUT%
    exit /b 1
)

:: デフォルト出力先(入力ファイルと同じフォルダ)
if not defined OUTPUT set "OUTPUT=%~dp1"
if not exist "%OUTPUT%\" mkdir "%OUTPUT%"

if "%VERBOSE%"=="1" (
    echo [詳細] 入力: %INPUT%
    echo [詳細] 出力先: %OUTPUT%
)

:: 変換処理本体
copy "%INPUT%" "%OUTPUT%\%~n1_converted%~x1" >nul
echo [OK] 変換完了: %OUTPUT%\%~n1_converted%~x1

実践例2:複数ファイルをまとめて処理(可変長引数)

@echo off
setlocal enabledelayedexpansion

:: ===================================
:: archive.bat ファイル1 ファイル2 ...
:: ===================================

if "%~1"=="" (
    echo 使い方: %~nx0 ファイル1 [ファイル2 ...]
    exit /b 1
)

set "ARCHIVE=C:\work\archive_%date:~0,4%%date:~5,2%%date:~8,2%.zip"
set "TMP_LIST=C:\work\tmp_filelist.txt"
if exist "%TMP_LIST%" del "%TMP_LIST%"

:: すべての引数をリストに追加
set "CNT=0"
:add_loop
if "%~1"=="" goto :add_done
if not exist "%~1" (
    echo [WARN] ファイルが見つかりません: %1
) else (
    echo %~1 >> "%TMP_LIST%"
    set /a CNT+=1
)
shift
goto :add_loop
:add_done

if !CNT! EQU 0 (
    echo [ERROR] 有効なファイルがありません
    exit /b 1
)

echo !CNT! 件のファイルをアーカイブします: %ARCHIVE%
powershell -NoProfile -Command "Compress-Archive -Path (Get-Content '%TMP_LIST%') -DestinationPath '%ARCHIVE%' -Force"
del "%TMP_LIST%" >nul
echo [OK] アーカイブ完了

実践例3:デプロイスクリプト(名前付き引数 + フラグ引数)

@echo off
setlocal enabledelayedexpansion

:: ============================================================
:: deploy.bat /env:production /src:C:\build /dryrun /verbose
:: ============================================================

set "ENV="
set "SRC="
set "DRYRUN=0"
set "VERBOSE=0"

:parse
if "%~1"=="" goto :end_parse
set "A=%~1"
if /i "!A:~0,5!"=="/env:"  ( set "ENV=!A:~5!"  & shift & goto :parse )
if /i "!A:~0,5!"=="/src:"  ( set "SRC=!A:~5!"  & shift & goto :parse )
if /i "!A!"=="/dryrun"     ( set "DRYRUN=1"     & shift & goto :parse )
if /i "!A!"=="/verbose"    ( set "VERBOSE=1"    & shift & goto :parse )
echo [WARN] 不明な引数: %1 (無視します)
shift & goto :parse
:end_parse

:: 必須引数チェック
if not defined ENV ( echo [ERROR] /env:環境名 を指定してください & exit /b 1 )
if not defined SRC ( echo [ERROR] /src:ソースパス を指定してください & exit /b 1 )
if not exist "%SRC%\" ( echo [ERROR] ソースパスが見つかりません: %SRC% & exit /b 1 )

if "%VERBOSE%"=="1" (
    echo ========== デプロイ設定 ==========
    echo 環境  : %ENV%
    echo ソース: %SRC%
    echo ドライラン: %DRYRUN%
    echo ==================================
)

if "%DRYRUN%"=="1" (
    echo [DRYRUN] xcopy "%SRC%\*" "D:\deploy\%ENV%\" /e /y
    exit /b 0
)

xcopy "%SRC%\*" "D:\deploy\%ENV%\" /e /y >nul
if errorlevel 1 ( echo [ERROR] デプロイ失敗 & exit /b 1 )
echo [OK] %ENV% にデプロイしました

11. まとめ

場面 使う構文・テクニック
基本的な引数受け取り %1 %2 %3
引用符を除く・パス展開 %~1 %~f1 %~dp1 %~nx1
全引数をまとめて %*(shift の影響を受けない)
可変長引数のループ shift + goto ループ
スペース含むパスの安全な受け渡し 渡し側: "パス" / 受け側: %~1
オプション(名前付き/フラグ)引数 shift ループで /key:value をパース
デフォルト値設定 if not defined でフォールバック
引数の存在・内容チェック if "%~1"=="" / if not exist "%~1"

FAQ

Q. 引数が10個以上になる場合はどうすればよいですか?
A. %1〜%9 は9個までしか直接アクセスできませんが、shift を使って順番に処理することで無制限に対応できます。shift ループで %1 を処理してから shift を呼ぶことで、10個目以降も %1 として受け取れます。
Q. 渡した引数の個数を取得したい。
A. バッチに引数の個数を返す組み込み変数はありません。shift ループで set /a CNT+=1 してカウントするか、for %%A in (%*) do set /a CNT+=1 で数えます。ただし %* に引用符付き引数が含まれる場合は for の動作に注意が必要です。
Q. サブルーチンの引数(%1〜)と外側の引数(%1〜)は衝突しませんか?
A. CALL でサブルーチンを呼ぶと %1〜%9 はサブルーチンへの引数に切り替わります。外側の引数はサブルーチン内からは参照できません。必要なら外側の引数を変数に保存(set "INPUT=%~1")してからサブルーチンを呼ぶ設計にしましょう。
Q. %~f1 で絶対パスに変換しましたが、存在しないパスが渡されると空になります。
A. %~f1 はファイルが存在しなくても絶対パスを生成しますが、カレントディレクトリを基準にするため、実行フォルダによって結果が変わります。意図したパスになっているか echo %~f1 で確認し、if not exist "%~f1" で存在チェックを加えてください。
Q. %0 でバッチファイルのフォルダを取得する方法は?
A. %~dp0 でバッチファイルが置かれているフォルダパス(末尾 \ 付き)を取得できます。これを使うと、バッチファイルからの相対パスで他のファイルを参照できます(例: set "CFG=%~dp0config.ini")。
Q. 引数のユーザー入力と set /p による対話入力はどちらが良いですか?
A. 自動化・バッチ処理の呼び出し元から値を渡す場合は引数が適しています。対話的に実行する場合や途中で確認が必要な場合は set /p によるユーザー入力が向いています。両方を組み合わせ「引数が空なら set /p で聞く」パターンも実務でよく使われます。