【bat】ファイルを別フォルダに移動する方法完全ガイド|move・forfiles・ワイルドカード・エラー処理・実践パターンまで

バッチファイルで「特定フォルダへの自動整理」「古いファイルを archive へ退避」「拡張子別にフォルダ振り分け」を実装する際、核心となるのが move コマンドです。本記事では move の基本構文から、ワイルドカード・forfiles を使った条件付き移動・エラー処理・実践パターンまで体系的に解説します。

この記事で解決できること

  • 単一ファイル・複数ファイルを move コマンドで移動する方法
  • ワイルドカードで拡張子・ファイル名パターンを指定して一括移動する方法
  • forfiles で「30日より古いファイル」などの日付条件を付けて移動する方法
  • 移動先フォルダが存在しない場合に自動作成して安全に移動する方法
  • ERRORLEVEL でエラーを検知してログに記録する方法
  • 拡張子別フォルダ分類・ファイル名日付付き移動などの実践パターン
スポンサーリンク

move コマンドの構文と主なオプション

move [/Y | /-Y] [source] [destination]

  /Y   : 移動先に同名ファイルが存在しても確認なしに上書き(既定の動作)
  /-Y  : 上書き前に確認プロンプトを表示する

  source      : 移動元ファイルのパス(ワイルドカード可)
  destination : 移動先フォルダのパス または 新しいファイル名
操作 構文例 説明
単一ファイル移動 move "srcfile.txt" "dest" file.txt を dest へ移動
リネームしながら移動 move "old.txt" "dest
ew.txt"
名前を変えて移動
ワイルドカード一括移動 move "src*.log" "dest" .log ファイルを全て移動
上書き確認あり move /-Y "src*" "dest" 同名ファイルに確認を出す
日付条件付き移動 forfiles /p src /m *.log /d -30 /c "cmd /c move @file dest" 30日以上古いファイルを移動
move はサブフォルダを再帰移動できない
move コマンドはファイルのみ対象で、サブフォルダ配下のファイルを再帰的に移動することはできません。
サブフォルダも含めて移動したい場合は ROBOCOPYxcopy でコピー後に元を削除する方法を使ってください。

方法1: 単一ファイルを移動する

基本: ファイルを指定フォルダへ移動

@echo off
setlocal

rem 単一ファイルを移動する
move "C:worksource
eport.txt" "C:workdest"

if errorlevel 1 (
    echo [ERROR] 移動に失敗しました
) else (
    echo [OK] 移動が完了しました
)

endlocal

リネームしながら移動する

@echo off
setlocal

rem 移動先で別の名前を付ける
set SRC=C:worksource
eport.txt
set DST=C:workdest
eport_backup.txt

move "%SRC%" "%DST%"

endlocal

方法2: ワイルドカードで複数ファイルを一括移動

特定の拡張子をまとめて移動

@echo off
setlocal

set SRC=C:worksource
set DST=C:workdest

rem .log ファイルを全て移動
move "%SRC%*.log" "%DST%"

if errorlevel 1 (
    echo [WARN] 移動対象ファイルがないか、一部失敗しました
) else (
    echo [OK] .log ファイルを %DST% へ移動しました
)

endlocal

ファイル名パターンで絞り込んで移動

@echo off
setlocal

set SRC=C:worksource
set DST=C:workdest

rem "report_" で始まるファイルを移動
move "%SRC%
eport_*.*" "%DST%"

rem "backup" を含む .zip ファイルを移動
move "%SRC%*backup*.zip" "%DST%"

endlocal

ワイルドカードのパターン詳細については バッチファイルでワイルドカードを使ってファイルを移動する方法 も参照してください。

方法3: forfiles で日付・サイズ条件付き移動

forfiles コマンドを使うと、作成日・更新日・ファイルサイズを条件に指定してファイルを移動できます。

30日より古いファイルを archive フォルダへ移動

@echo off
setlocal

set SRC=C:worklogs
set ARC=C:workarchive

rem archive フォルダがなければ作成
if not exist "%ARC%" mkdir "%ARC%"

rem 30日以上更新されていない .log ファイルを archive へ移動
forfiles /p "%SRC%" /m *.log /d -30 /c "cmd /c move @file "%ARC%""

if errorlevel 1 (
    echo 移動対象ファイルがありません(または全て新しいファイルです)
) else (
    echo archive 移動完了
)

endlocal

forfiles のオプション一覧

オプション 意味
/p path 対象フォルダのパス /p "C:logs"
/m mask ファイル名パターン /m *.log
/d -N N日以上古いファイル /d -30
/d +N N日以内の新しいファイル /d +7
/d YYYY/MM/DD 指定日より古いファイル /d 2025/01/01
/s サブフォルダも再帰的に検索 (単独指定)
/c "cmd /c ..." 各ファイルに実行するコマンド /c "cmd /c move @file "dest""
@file には自動でダブルクォートが付く
forfiles@file 変数はファイルパスをダブルクォート付きで展開します。
そのため move @file "dest" と書けばスペースを含むパスでも正しく動作します。
移動先のパスもダブルクォートで囲んでおくのが確実です。

方法4: for /f + dir で絞り込んで移動

更新日順・ファイルサイズ順などで並べ替えてから処理したい場合は dir の出力を for /f で受け取る方法が使えます。

更新日が古い順に移動(最新10件を残す)

@echo off
setlocal enabledelayedexpansion

set SRC=C:worklogs
set ARC=C:workarchive
set KEEP=10

if not exist "%ARC%" mkdir "%ARC%"

rem ファイル数を数える
set TOTAL=0
for %%F in ("%SRC%*.log") do set /a TOTAL+=1

rem KEEP 件より多い場合、古い順に超過分を archive へ移動
set MOVED=0
set /a LIMIT=%TOTAL%-%KEEP%

if !LIMIT! LEQ 0 (
    echo 移動不要: ファイル数 %TOTAL% は KEEP=%KEEP% 以内です
    goto :end
)

rem /o:d = 更新日の古い順
for /f "delims=" %%F in ('dir /b /a-d /o:d "%SRC%*.log"') do (
    if !MOVED! LSS !LIMIT! (
        move "%SRC%\%%F" "%ARC%" >nul
        echo 移動: %%F
        set /a MOVED+=1
    )
)

echo 完了: %MOVED% ファイルを archive に移動しました
:end
endlocal

移動先フォルダが存在しない場合に自動作成する

移動先フォルダが存在しないと move はエラーになります。if not exist ... mkdir で事前に作成するのが定石です。

@echo off
setlocal

set SRC=C:worksource
set DST=C:workdest

rem 移動先が存在しない場合は作成
if not exist "%DST%" (
    mkdir "%DST%"
    echo フォルダを作成しました: %DST%
)

rem ファイルを移動
move "%SRC%*.*" "%DST%"

endlocal

フォルダの存在確認については IF EXISTでファイル・フォルダの存在確認をする方法、フォルダの自動作成については フォルダが存在しない場合のみ作成する方法 も参照してください。

ERRORLEVEL でエラーを確認する

@echo off
setlocal

set SRC=C:worksource
set DST=C:workdest

move "%SRC%*.log" "%DST%"

rem move のエラーコード:
rem   0 = 成功
rem   1 = 対象ファイルが存在しない、または移動失敗

if errorlevel 1 (
    echo [ERROR] 移動に失敗しました(ファイルが存在しないか、アクセスできません)
    exit /b 1
) else (
    echo [OK] 移動完了
)

endlocal
move は移動対象がゼロ件でも ERRORLEVEL=1 を返す
移動するファイルが1件もない場合、move は “ファイルが見つかりません” と表示して ERRORLEVEL=1 を返します。
“対象なし” と “真のエラー” を区別したい場合は、移動前に if exist "%SRC%*.log" で存在確認してから移動してください。

実践例A: 拡張子別にサブフォルダへ自動分類する

@echo off
setlocal

set SRC=C:worksource
set BASE=C:worksorted

rem 各拡張子の移動先を作成して移動
for %%E in (txt csv log pdf zip) do (
    if exist "%SRC%*.%%E" (
        if not exist "%BASE%\%%E" mkdir "%BASE%\%%E"
        move "%SRC%*.%%E" "%BASE%\%%E"
        echo .%%E ファイルを %BASE%\%%E へ移動しました
    )
)

echo 分類完了
endlocal

特定の拡張子をまとめて移動する方法の詳細は バッチファイルで特定の拡張子だけをまとめて移動する方法 を参照してください。

実践例B: 30日より古いログを archive フォルダへ日付付きで退避

@echo off
setlocal

set LOGDIR=C:worklogs
set ARC=%LOGDIR%archive\%DATE:~0,4%-%DATE:~5,2%-%DATE:~8,2%

rem 日付付き archive サブフォルダを作成
if not exist "%ARC%" mkdir "%ARC%"

rem 30日以上古い .log を archive へ移動
forfiles /p "%LOGDIR%" /m *.log /d -30 /c "cmd /c move @file "%ARC%""

if errorlevel 1 (
    echo 対象ファイルなし(30日以内のファイルのみ)
) else (
    echo archive 移動完了: %ARC%
)

endlocal

実践例C: ファイル名に移動日を付けてリネームしながら移動

@echo off
setlocal enabledelayedexpansion

set SRC=C:worksource
set DST=C:workdest

if not exist "%DST%" mkdir "%DST%"

rem 今日の日付を取得(YYYYMMDD 形式)
set TODAY=%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%

rem 各ファイルを "元の名前_YYYYMMDD.拡張子" にリネームしながら移動
for %%F in ("%SRC%*.txt") do (
    set NEWNAME=%%~nF_!TODAY!%%~xF
    move "%%~fF" "%DST%!NEWNAME!"
    echo 移動: %%~nxF → !NEWNAME!
)

endlocal

実践例D: 移動結果をCSVログに記録する

@echo off
setlocal enabledelayedexpansion

set SRC=C:worksource
set DST=C:workdest
set CSVLOG=%~dp0move_log.csv

if not exist "%DST%" mkdir "%DST%"

rem CSV ヘッダー(初回のみ)
if not exist "%CSVLOG%" (
    echo 日時,ファイル名,移動元,移動先,結果 > "%CSVLOG%"
)

for %%F in ("%SRC%*.log") do (
    move "%%~fF" "%DST%"
    if errorlevel 1 (
        set RESULT=FAIL
    ) else (
        set RESULT=OK
    )
    echo %DATE% %TIME%,%%~nxF,%SRC%,%DST%,!RESULT! >> "%CSVLOG%"
    echo !RESULT! : %%~nxF
)

endlocal

よくある落とし穴

落とし穴1: パスにスペースがある場合はダブルクォート必須

rem NG: スペースを含むパスをクォートなしで指定するとエラー
move C:My Folder
eport.txt C:dest folder
rem → "C:My" が1つ目の引数、"Folder
eport.txt" が2つ目として解釈される

rem OK: ダブルクォートで囲む
move "C:My Folder
eport.txt" "C:dest folder"

落とし穴2: 移動先フォルダと同名ファイルが存在すると上書きされる

rem move の既定動作は /Y(確認なしで上書き)
rem 上書きを防ぎたい場合は事前に存在確認する

set DST_FILE=C:workdest
eport.txt

if exist "%DST_FILE%" (
    echo [SKIP] 移動先に同名ファイルが存在します: %DST_FILE%
) else (
    move "C:worksource
eport.txt" "C:workdest"
)

落とし穴3: ドライブをまたぐ移動は時間がかかる場合がある

rem 同一ドライブ内の move はディレクトリエントリの書き換えだけなので高速
rem 別ドライブへの move は コピー + 削除 になるため大きなファイルは時間がかかる

rem 例: C: → D: への移動(実態はコピー+削除)
move "C:workdata.bak" "D:ackup"

rem 大量ファイルを別ドライブへ移動する場合は ROBOCOPY /mov が高効率
robocopy "C:work" "D:ackup" *.bak /mov /r:2 /w:5

落とし穴4: for ループ内で move の ERRORLEVEL を判定する際の注意

rem NG: %ERRORLEVEL% はブロック解析時に展開されるため常に古い値を参照する
@echo off
setlocal
for %%F in (*.log) do (
    move "%%F" "dest"
    if %ERRORLEVEL% == 0 echo OK: %%F   ← 常に 0 になる可能性がある
)

rem OK: enabledelayedexpansion + if errorlevel を使う
@echo off
setlocal enabledelayedexpansion
for %%F in (*.log) do (
    move "%%F" "dest"
    if not errorlevel 1 echo OK: %%F
)

落とし穴5: ファイルが使用中だと move が失敗する

rem 別プロセスが開いているファイルは move できない(アクセス拒否エラー)
rem 対処1: ファイルを開いているプロセスを特定して終了してから移動
rem 対処2: ロックが解除されるまでリトライループで待機

set RETRY=0
:retry
move "C:worksource
eport.txt" "C:workdest" >nul 2>&1
if errorlevel 1 (
    set /a RETRY+=1
    if %RETRY% LSS 5 (
        echo [WAIT] ファイルがロックされています。3秒後にリトライ... (%RETRY%/5)
        timeout /t 3 /nobreak >nul
        goto :retry
    ) else (
        echo [ERROR] 移動に失敗しました(ファイルがロックされています)
        exit /b 1
    )
) else (
    echo [OK] 移動完了
)

よくある質問(FAQ)

Q サブフォルダも含めて再帰的にファイルを移動したい
A

move は再帰移動ができません。robocopy /mov を使うと、サブフォルダ構造ごとファイルを移動できます。

rem /mov = ファイルを移動(コピー後に移動元を削除)
rem /e  = 空サブフォルダも含めて再帰コピー
robocopy "C:worksource" "C:workdest" /mov /e /r:2 /w:5

rem 特定拡張子のみ再帰移動
robocopy "C:worksource" "C:workdest" *.log /mov /s /r:2 /w:5

ROBOCOPY の詳細は ROBOCOPYの使い方完全ガイド を参照してください。

Q 移動したファイルの件数を表示したい
A

移動前後のファイル数の差、または for ループ内でカウントする方法が使えます。

@echo off
setlocal enabledelayedexpansion

set SRC=C:worksource
set DST=C:workdest

if not exist "%DST%" mkdir "%DST%"

set COUNT=0
for %%F in ("%SRC%*.log") do (
    move "%%~fF" "%DST%" >nul
    if not errorlevel 1 set /a COUNT+=1
)

echo 移動完了: !COUNT! 件
Q 移動元と移動先が同じドライブかどうかで処理を分けたい
A

パス変数からドライブ文字を取り出して比較します。

@echo off
setlocal

set SRC=C:worksource
set DST=D:ackup

rem 先頭2文字(ドライブ文字 + コロン)を比較
if /i "%SRC:~0,2%" == "%DST:~0,2%" (
    echo 同一ドライブ: move は高速(メタデータ更新のみ)
) else (
    echo 別ドライブ: move はコピー+削除のため時間がかかります
)

move "%SRC%*.*" "%DST%"

endlocal
Q 移動先に同名ファイルがある場合にバックアップを取ってから上書きしたい
A

移動前に移動先の同名ファイルをリネームしてバックアップします。

@echo off
setlocal

set SRC_FILE=C:worksource
eport.txt
set DST_DIR=C:workdest
set DST_FILE=%DST_DIR%
eport.txt

rem 同名ファイルが存在する場合は .bak にリネームしてから移動
if exist "%DST_FILE%" (
    move "%DST_FILE%" "%DST_FILE%.bak"
    echo バックアップ作成: %DST_FILE%.bak
)

move "%SRC_FILE%" "%DST_DIR%"
echo 移動完了

endlocal
Q ファイルサイズが 0 のファイルだけ別フォルダに移動したい
A

%%~zF でファイルサイズ(バイト)を取得し、0 と比較します。

@echo off
setlocal

set SRC=C:worksource
set EMPTY_DIR=C:workempty_files

if not exist "%EMPTY_DIR%" mkdir "%EMPTY_DIR%"

for %%F in ("%SRC%*.*") do (
    if %%~zF == 0 (
        move "%%~fF" "%EMPTY_DIR%"
        echo 空ファイルを移動: %%~nxF
    )
)

endlocal

ファイルサイズを条件に使う方法の詳細は バッチファイルでファイルサイズを取得して条件分岐する方法 を参照してください。

まとめ

目的 推奨方法
単一ファイルを移動 move "srcfile.txt" "dest"
ワイルドカードで一括移動 move "src*.log" "dest"
リネームしながら移動 move "srcold.txt" "dest
ew.txt"
日付条件付き移動 forfiles /p src /m *.log /d -30 /c "cmd /c move @file dest"
移動先フォルダ自動作成 移動前に if not exist "%DST%" mkdir "%DST%"
エラー確認 if not errorlevel 1 または if errorlevel 1
サブフォルダ含む再帰移動 robocopy "src" "dest" /mov /e
拡張子別フォルダへ分類 for %%E in (txt csv log) do move "src*.%%E" "dest\%%E"

ファイル操作の関連記事として、複数フォルダへの一括処理は 複数フォルダをループして一括処理する方法 を、特定の文字列を含むファイルの移動は 特定の文字列を含むファイルを別フォルダにコピーする方法 も参照してください。