バッチファイル(bat)でファイルを一括移動するとき、ワイルドカード(* と ?)を使えば、拡張子・ファイル名のパターンで一度に大量のファイルを移動できます。本記事では基本の move コマンドから、サブフォルダ再帰・robocopy /MOV・ドライランまで、実務で使える移動パターンを体系的に解説します。
この記事でできること
*・?ワイルドカードでパターンに合うファイルを一括移動- 拡張子指定・プレフィックス・日付パターンなど多様な条件で絞り込む
- サブフォルダを再帰的にたどって移動する(for /r + move)
- robocopy /MOV でコピー後に元ファイルを削除する安全な移動
- ドライランで実行前に対象ファイルを確認する
- 実践例3本(バックアップ整理・ログ月別移動・一時ファイル削除)と落とし穴・FAQ も解説
ワイルドカードの基本
bat で使えるワイルドカードは *(任意の文字列)と ?(任意の1文字)の2種類です。
| 記号 | 意味 | マッチ例 | 非マッチ例 |
|---|---|---|---|
* |
0文字以上の任意の文字列 | *.txt → a.txt, report_2024.txt |
*.txt → a.csv |
? |
任意の1文字(必ず1文字) | file?.txt → file1.txt, fileA.txt |
file?.txt → file12.txt |
*.* |
拡張子を持つ全ファイル | data.csv, log.txt | (拡張子なしファイル) |
????.* |
4文字のファイル名(任意の拡張子) | data.csv, logs.txt | ab.txt, report.txt |
move コマンドは移動先に同名ファイルがあると確認なしに上書きします。重要ファイルを扱う場合は必ずドライランで確認するか、robocopy /MOV の /XO(古いファイルのみ移動)や /L(一覧表示のみ)オプションを活用してください。
方法1: 拡張子・プレフィックスで一括移動
move コマンドに直接ワイルドカードを指定する最もシンプルな方法です。
特定の拡張子のファイルをすべて移動
@echo off setlocal set "SRC=C:\work\source" set "DST=C:\work\dest" if not exist "%DST%" mkdir "%DST%" rem .txt ファイルをすべて移動 move "%SRC%\*.txt" "%DST%\" echo 完了 endlocal
ファイル名がプレフィックスで始まるファイルを移動
@echo off setlocal set "SRC=C:\logs" set "DST=C:\archive\logs" if not exist "%DST%" mkdir "%DST%" rem "error_" で始まるすべての .log ファイルを移動 move "%SRC%\error_*.log" "%DST%\" echo エラーログを移動しました endlocal
複数の拡張子を対象にする(ループ)
@echo off
setlocal
set "SRC=C:\work"
set "DST=C:\archive"
if not exist "%DST%" mkdir "%DST%"
rem .bak .tmp .log をそれぞれ移動
for %%E in (bak tmp log) do (
move "%SRC%\*.%%E" "%DST%\" >nul 2>&1
)
echo 完了
endlocal
移動後にファイルの拡張子も変えたい場合は bat でファイルの拡張子を一括変換する方法 も合わせて参照してください。
方法2: ? で文字数固定パターンの移動
? は必ず1文字にマッチします。連番・日付・バージョン番号など文字数が決まっているパターンに有効です。
4文字の連番ファイルを移動
@echo off setlocal set "SRC=C:\data" set "DST=C:\done" if not exist "%DST%" mkdir "%DST%" rem report_0001.csv ~ report_9999.csv にマッチ move "%SRC%\report_????.csv" "%DST%\" echo 完了 endlocal
日付パターン(YYYYMMDD)のファイルを移動
@echo off setlocal set "SRC=C:\backup" set "DST=C:\old_backup" if not exist "%DST%" mkdir "%DST%" rem backup_20231231.zip のような 8桁日付パターン move "%SRC%\backup_????????.zip" "%DST%\" echo バックアップファイルをアーカイブに移動しました endlocal
方法3: ドライランで移動前に対象ファイルを確認
実際に移動する前に、どのファイルが対象になるかを確認します。-dry オプションで確認のみ実行し、問題なければそのまま移動できます。
@echo off
setlocal enabledelayedexpansion
set "SRC=C:\work\logs"
set "DST=C:\archive\logs"
set "PATTERN=*.log"
set "DRY=0"
if "%1"=="-dry" set "DRY=1"
set /a COUNT=0
for %%F in ("%SRC%\%PATTERN%") do (
set /a COUNT+=1
if !DRY!==1 (
echo [DRY] %%~nxF → %DST%
) else (
if not exist "%DST%" mkdir "%DST%"
move "%%F" "%DST%\"
)
)
if !DRY!==1 (
echo [DRY] 対象ファイル数: !COUNT! 件(実際には移動しません)
) else (
echo 移動完了: !COUNT! 件
)
endlocal
方法4: サブフォルダを再帰的にたどって移動(for /r)
move コマンドはサブフォルダを自動でたどりません。for /r でサブフォルダを再帰的に検索し、マッチするファイルを移動します。
サブフォルダ内の .tmp ファイルをすべて移動
@echo off
setlocal enabledelayedexpansion
set "ROOT=C:\project"
set "DST=C:\cleanup\tmp_files"
if not exist "%DST%" mkdir "%DST%"
set /a COUNT=0
for /r "%ROOT%" %%F in (*.tmp) do (
set /a COUNT+=1
move "%%F" "%DST%\"
)
echo 再帰移動完了: !COUNT! 件
endlocal
サブフォルダ構造を維持しながら移動(相対パス保持)
移動先でも元のサブフォルダ構造を保ちたい場合は、%%~dpF(フォルダパス)を利用してサブフォルダを再現します。
@echo off
setlocal enabledelayedexpansion
set "ROOT=C:\src"
set "DST_ROOT=C:\dst"
for /r "%ROOT%" %%F in (*.log) do (
rem 元フォルダの相対部分を取り出す
set "SRCDIR=%%~dpF"
set "REL=!SRCDIR:%ROOT%=!"
set "DESTDIR=%DST_ROOT%!REL!"
if not exist "!DESTDIR!" mkdir "!DESTDIR!"
move "%%F" "!DESTDIR!"
)
echo フォルダ構造を維持して移動完了
endlocal
方法5: robocopy /MOV で安全な移動
robocopy はコピー後に元ファイルを削除する方式のため、move より安全です。ネットワーク越しの移動・再試行・ログ記録など運用向け機能も豊富です。
基本: *.log を移動(/MOV)
@echo off setlocal set "SRC=C:\logs\raw" set "DST=C:\logs\archive" rem /MOV: ファイルを移動(コピー後に削除) rem /NFL /NDL: ファイル名リスト表示なし rem /NJH /NJS: ヘッダー・サマリー非表示 robocopy "%SRC%" "%DST%" *.log /MOV /NFL /NDL /NJH /NJS echo robocopy 移動完了 endlocal
サブフォルダも含めて再帰移動(/MOV /S)
@echo off setlocal set "SRC=C:\project\output" set "DST=C:\archive\output" rem /MOV /S: サブフォルダを含めて移動 rem /XO: 移動先の方が新しければスキップ(上書き防止) robocopy "%SRC%" "%DST%" *.zip /MOV /S /XO echo アーカイブへの移動完了 endlocal
ドライラン確認(/L オプション)
@echo off rem /L: 実際には移動せず、対象ファイルの一覧だけ表示 robocopy "C:\src" "C:\dst" *.tmp /MOV /L echo 上記が移動対象です(実際には移動していません)
move: シンプルで高速。同一ドライブ内なら実質リネーム(ファイルのコピーが発生しない)。robocopy /MOV: ドライブ跨ぎ・ネットワーク越し・再試行・ログ記録が必要な場合に使う。
実践例A: 古いバックアップZIPを日付パターンで整理
想定: backup_YYYYMMDD.zip の形式でバックアップが溜まっている。指定した年月(YYYY-MM)のファイルを別フォルダへ移動します。
@echo off
setlocal enabledelayedexpansion
set "SRC=C:\backup\daily"
set "DST_BASE=C:\backup\monthly"
rem 移動対象の年月を指定(YYYYMM)
set "TARGET_YM=202401"
set "DST=%DST_BASE%\%TARGET_YM%"
if not exist "%DST%" mkdir "%DST%"
set /a MOVED=0
for %%F in ("%SRC%\backup_%TARGET_YM%??.zip") do (
move "%%F" "%DST%\"
set /a MOVED+=1
)
echo ========================
echo %TARGET_YM% のバックアップ整理完了
echo 移動件数: %MOVED% 件
echo 移動先 : %DST%
echo ========================
endlocal
特定の文字列を含むファイルだけを選んでコピー・移動する方法は bat で特定の文字列を含むファイルをコピーする方法 も合わせてご覧ください。
実践例B: 30日以上前のログファイルを月別フォルダへ移動
forfiles コマンドで更新日が古いファイルだけを対象にし、月別サブフォルダへ振り分けます。
bat の
%DATE% は OS のロケール設定によって形式が異なるため、月の抽出に使うと環境依存の問題が起きやすいです。PowerShell の Get-Item.LastWriteTime はロケール非依存で信頼性が高いため、ここでは PowerShell を利用しています。また、ファイルパスにスペースや特殊文字が含まれる場合は %%~F のクォート処理が崩れることがあります。その場合は PowerShell スクリプト単体で実装することをお勧めします。
@echo off
setlocal enabledelayedexpansion
set "SRC=C:\app\logs"
set "DST_BASE=C:\app\logs\archive"
if not exist "%DST_BASE%" mkdir "%DST_BASE%"
set /a MOVED=0
rem 30日以上前の .log ファイルを月別フォルダへ移動
for /f "usebackq tokens=*" %%F in (`forfiles /p "%SRC%" /m *.log /d -30 /c "cmd /c echo @path"`) do (
rem ファイル更新年月を取得(PowerShell 経由)
for /f "usebackq tokens=*" %%M in (`powershell -NoProfile -Command "(Get-Item '%%~F').LastWriteTime.ToString('yyyyMM')"`) do (
set "MONTH=%%M"
)
set "DST=%DST_BASE%\!MONTH!"
if not exist "!DST!" mkdir "!DST!"
move "%%~F" "!DST!\"
set /a MOVED+=1
)
echo 古いログ移動完了: %MOVED% 件
endlocal
実践例C: ビルド成果物を種別フォルダに振り分けて移動
ビルドフォルダに混在する .exe・.dll・.pdb をそれぞれの種別フォルダに振り分け、整理します。
@echo off
setlocal enabledelayedexpansion
set "SRC=C:\build\output"
set "DST_BASE=C:\release"
rem 種別ごとに移動先フォルダを定義
set "DST_EXE=%DST_BASE%\bin"
set "DST_DLL=%DST_BASE%\lib"
set "DST_PDB=%DST_BASE%\symbols"
for %%D in ("%DST_EXE%" "%DST_DLL%" "%DST_PDB%") do (
if not exist %%D mkdir %%D
)
move "%SRC%\*.exe" "%DST_EXE%\"
move "%SRC%\*.dll" "%DST_DLL%\"
move "%SRC%\*.pdb" "%DST_PDB%\"
echo ビルド成果物の振り分け完了
echo exe → %DST_EXE%
echo dll → %DST_DLL%
echo pdb → %DST_PDB%
endlocal
よくある落とし穴
落とし穴1: 移動先に同名ファイルがあると確認なしに上書き
move は移動先に同名ファイルがあるとき、確認ダイアログなしで上書きします。上書きを防ぐには事前に存在チェックを入れるか、robocopy /XO(古いものだけ移動)を使います。
@echo off
setlocal enabledelayedexpansion
set "SRC=C:\src"
set "DST=C:\dst"
for %%F in ("%SRC%\*.txt") do (
if exist "%DST%\%%~nxF" (
echo [SKIP] %%~nxF は移動先に存在するためスキップ
) else (
move "%%F" "%DST%\"
)
)
endlocal
落とし穴2: move はサブフォルダを再帰的に処理しない
move *.txt dest\ はカレントフォルダのファイルのみ対象です。サブフォルダも含めて移動するには for /r か robocopy /S を使います。
rem NG: サブフォルダは処理されない move "C:\src\*.txt" "C:\dst\" rem OK: for /r でサブフォルダを再帰処理 for /r "C:\src" %%F in (*.txt) do move "%%F" "C:\dst\"
落とし穴3: パスにスペースが含まれるとエラー
フォルダ名やファイル名にスペースがある場合、move の引数を " " で囲まないとエラーになります。ワイルドカードも含めて常にダブルクォートで囲む習慣を付けましょう。
rem NG: スペースがあるパスはエラー move C:\My Documents\*.txt C:\Archive\ rem OK: ダブルクォートで囲む move "C:\My Documents\*.txt" "C:\Archive\"
落とし穴4: 移動先フォルダが存在しないとエラー
move は移動先フォルダが存在しない場合エラーになります。if not exist ... mkdir で事前に作成してください。
@echo off set "DST=C:\archive\2024" rem フォルダがなければ作成してから移動 if not exist "%DST%" mkdir "%DST%" move "C:\work\*.bak" "%DST%\"
落とし穴5: ? は1文字にのみマッチ・パスセパレータをまたがない
? は必ず1文字にマッチします。file?.txt は file1.txt(1文字)にはマッチしますが、file12.txt(2文字)にはマッチしません。また ? は拡張子の . もパスセパレータ(\)も「1文字」としてカウントされるため、拡張子パターンの組み合わせに注意が必要です。*.txt の * もパスセパレータをまたがず、カレントフォルダ内のみが対象です。サブフォルダは for /r で対応します。
rem file?.txt は file1.txt にマッチ、file12.txt にはマッチしない move "C:\data\file?.txt" "C:\done\" rem *.* は拡張子を持つファイル全て move "C:\data\*.*" "C:\done\" rem * だけだと拡張子なしファイルもマッチ(*.* との違いに注意) move "C:\data\*" "C:\done\"
よくある質問(FAQ)
for ループでカウンタを使うか、robocopy のサマリーを利用します。
@echo off
setlocal enabledelayedexpansion
set /a COUNT=0
for %%F in ("C:\src\*.txt") do (
move "%%F" "C:\dst\"
set /a COUNT+=1
)
echo 移動完了: !COUNT! 件
endlocal
move コマンドは対象ファイルがない場合に”ファイルが見つかりません”を表示してエラーレベルを返します。for ループを使えば対象ゼロでも何も起きません。
@echo off
setlocal enabledelayedexpansion
set /a COUNT=0
for %%F in ("C:\src\*.tmp") do (
move "%%F" "C:\dst\" >nul
set /a COUNT+=1
)
if !COUNT!==0 (
echo 移動対象のファイルはありませんでした
) else (
echo !COUNT! 件移動しました
)
endlocal
移動したファイル名と日時を別ファイルに記録します。
@echo off
setlocal enabledelayedexpansion
set "SRC=C:\src"
set "DST=C:\dst"
set "LOG=move_log.txt"
for %%F in ("%SRC%\*.log") do (
move "%%F" "%DST%\"
echo %DATE% %TIME% MOVED %%~nxF >> "%LOG%"
)
echo ログ: %LOG%
endlocal
同一ドライブ内の move はディレクトリエントリの変更だけで完了するため高速ですが、別ドライブへの移動は実際にファイルをコピーしてから削除するため時間がかかります。これは正常な動作です。大量ファイルの別ドライブ移動には robocopy /MOV /MT(マルチスレッド)が高速です。
rem マルチスレッド(/MT:8)で別ドライブへ高速移動 robocopy "C:\src" "D:\dst" *.* /MOV /MT:8 /NFL /NDL
move は移動と同時にリネームもできます。ただしワイルドカードを使いながらリネームする場合、同名の場合は上書きになる点に注意です。
@echo off
setlocal enabledelayedexpansion
rem ファイルを移動しながらプレフィックスを付けてリネーム
for %%F in ("C:\src\*.txt") do (
move "%%F" "C:\dst\archive_%%~nxF"
)
echo 移動・リネーム完了
endlocal
読み取り専用属性が付いているファイルは move でエラーになることがあります。attrib -r で属性を解除してから移動してください。
@echo off setlocal set "SRC=C:\src" set "DST=C:\dst" rem 読み取り専用属性を解除してから移動 attrib -r "%SRC%\*.bak" move "%SRC%\*.bak" "%DST%\" echo 完了 endlocal
まとめ
| 目的 | 推奨方法 | ポイント |
|---|---|---|
| 拡張子指定で一括移動 | move “src\*.ext” “dst\” | シンプル・同一ドライブなら高速 |
| ファイル名パターンで移動 | move “prefix_*.*” / “file????.csv” | * と ? を組み合わせて柔軟に絞り込み |
| サブフォルダ再帰移動 | for /r + move | move 単体はサブフォルダ非対応 |
| 安全な移動(上書き制御) | robocopy /MOV /XO | コピー後削除・上書きスキップ対応 |
| 実行前の確認 | for + echo(ドライラン)/ robocopy /L | 本番前に必ず確認 |
| 別ドライブへの大量移動 | robocopy /MOV /MT:8 | マルチスレッドで高速化 |
単発の移動なら move、サブフォルダ・大量ファイル・上書き制御が必要なら robocopy /MOV を選ぶのが基本方針です。本番実行前は必ずドライランで対象を確認しましょう。
ファイルの拡張子を一括で変更するには bat でファイルの拡張子を一括変換する方法 を、特定の文字列を含むファイルを別フォルダにコピーするには bat で特定の文字列を含むファイルをコピーする方法 も合わせて参照してください。

