【bat】複数フォルダをループして一括処理する方法|for /d・for /r・ネストループ・実践パターン完全ガイド

【bat】複数フォルダをループして一括処理する方法|for /d・for /r・ネストループ・実践パターン完全ガイド bat

「複数のフォルダに対して同じ処理を繰り返したい」「サブフォルダ内のファイルをまとめて移動したい」——バッチファイルでフォルダをループ処理する場面は多くあります。本記事では for /dfor /r・指定リストの3つのループ構文を軸に、ネストループ・実践パターン・よくある落とし穴まで体系的に解説します。

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

  • カレントディレクトリのサブフォルダ全体を for /d でループする方法
  • for /r で全階層のフォルダ・ファイルを再帰的に処理する方法
  • コード内リスト・テキストファイルからフォルダを読み込んでループする方法
  • フォルダ→ファイルのネストループで一括処理する実践スクリプト
  • パスにスペースがある場合の対処・遅延展開が必要な場面
スポンサーリンク

ループ方法の比較

構文 対象 特徴
for /d %%F in (*) do カレントの直下サブフォルダ シンプル・最もよく使う
for /d %%F in (C:ase*) do 指定パスの直下サブフォルダ カレント以外のパスを対象にできる
for /r %%F in (.) do 全階層のサブフォルダ(再帰) 深いフォルダ構造を一括処理
for %%F in (A B C) do コード内に列挙したフォルダ 対象フォルダが固定の場合に便利
for /f %%F in (list.txt) do テキストファイルのフォルダリスト 対象が多い場合・動的変更しやすい
%~fF・%~nF・%~xF — パス修飾子を活用しよう
%%F に修飾子を付けると、フルパス・フォルダ名・ドライブ名などを個別に取得できます。
%%~fF=フルパス、%%~nF=名前のみ(拡張子なし)、%%~dpF=ドライブ+ディレクトリ部分。
パスにスペースが含まれる場合は "%%~fF" のようにダブルクォートで囲むのが基本です。

方法1: for /d でカレントのサブフォルダをループ

最も基本的なパターンです。カレントディレクトリ直下にあるすべてのフォルダに対してループします。

基本構文

@echo off
setlocal

rem カレントディレクトリの直下サブフォルダをループ
for /d %%F in (*) do (
    echo フォルダ名: %%F
    echo フルパス : %%~fF
)

endlocal

特定パスの直下サブフォルダを対象にする

@echo off
setlocal

set BASE=C:workprojects

rem 指定パス直下のサブフォルダをループ
for /d %%F in ("%BASE%*") do (
    echo フォルダ名: %%~nxF
    echo フルパス : %%~fF
)

endlocal
for /d は直下のサブフォルダのみ対象
for /d %%F in (*) do はカレントの 直下1階層 のフォルダだけを対象にします。
さらに深い階層(孫フォルダ以降)も処理したい場合は for /r を使ってください。

方法2: for /r で全階層を再帰探索

ルートフォルダ以下のすべての階層を再帰的にたどります。深いフォルダ構造を一括処理する場合に使います。

全サブフォルダを再帰的にループ

@echo off
setlocal enabledelayedexpansion

set ROOT=C:workdata

rem ROOT 配下の全フォルダ(全階層)をループ
for /r "%ROOT%" %%F in (.) do (
    rem %%F は "フォルダ内の ." = そのフォルダ自体を指す
    rem %%~dpF でディレクトリパスを取得
    set DIRPATH=%%~dpF
    rem 末尾の  を除去してフォルダ名を取得
    set DIRPATH=!DIRPATH:~0,-1!
    echo サブフォルダ: !DIRPATH!
)

endlocal
for /r %%F in (.) の注意点
%%F の値は C:workdatasub. のように末尾に . が付きます。
フォルダパスとして使うには %%~dpF(末尾 付き)か、setlocal enabledelayedexpansion で末尾を除去して使います。
詳しくは setlocal enabledelayedexpansion 完全ガイド を参照してください。

全階層の特定拡張子ファイルを再帰検索

@echo off
setlocal

set ROOT=C:workdata

rem ROOT 配下のすべての .log ファイルを再帰的に列挙
for /r "%ROOT%" %%F in (*.log) do (
    echo ログファイル: %%~fF
    echo  サイズ: %%~zF バイト
)

endlocal

方法3: 指定したフォルダのリストをループ

処理対象フォルダが固定で決まっている場合は、コード内にリストを書く方法が読みやすくて保守しやすいです。

コード内にフォルダリストを記述

@echo off
setlocal

rem 処理したいフォルダをスペース区切りで列挙
for %%F in (
    "C:workproject_A"
    "C:workproject_B"
    "C:workproject_C"
) do (
    if exist %%F (
        echo 処理中: %%~F
    ) else (
        echo スキップ(存在しない): %%~F
    )
)

endlocal

テキストファイルからフォルダリストを読み込む

対象フォルダが多い場合や動的に変更したい場合は、外部テキストファイルで管理します。

@echo off
setlocal

rem folder_list.txt の内容(1行1フォルダ)
rem C:workproject_A
rem C:workproject_B
rem C:workproject_C

set LISTFILE=%~dp0folder_list.txt

if not exist "%LISTFILE%" (
    echo エラー: %LISTFILE% が見つかりません
    exit /b 1
)

for /f "usebackq delims=" %%F in ("%LISTFILE%") do (
    if exist "%%F" (
        echo 処理中: %%F
    ) else (
        echo スキップ(存在しない): %%F
    )
)

endlocal
usebackq とは?
for /f "usebackq" を指定すると、バッククォート `command` でコマンド実行、ダブルクォート "filename" でファイル読み込みができます。
ファイルパスにスペースが含まれる場合に "usebackq" が必要になります。

ネストループ: フォルダ→ファイルを一括処理

「各サブフォルダの中にあるファイルを処理する」という最も多いパターンです。

各サブフォルダの全ファイルを処理

@echo off
setlocal

rem カレントの各サブフォルダに入り、全ファイルを列挙
for /d %%F in (*) do (
    echo === フォルダ: %%F ===
    for %%G in ("%%F*") do (
        echo   ファイル: %%~nxG  サイズ: %%~zG バイト
    )
)

endlocal

各サブフォルダの特定拡張子ファイルのみ処理

@echo off
setlocal

for /d %%F in (*) do (
    echo === フォルダ: %%~nxF ===
    for %%G in ("%%F*.csv") do (
        echo   CSV: %%~nxG
    )
)

endlocal

実践例A: 各フォルダの.txtファイルを1か所に収集する

@echo off
setlocal

set DEST=%~dp0collected_txt
if not exist "%DEST%" mkdir "%DEST%"

set COUNT=0

for /d %%F in (*) do (
    for %%G in ("%%F*.txt") do (
        echo コピー: %%~fG  →  %DEST%
        copy "%%~fG" "%DEST%" >nul
        set /a COUNT+=1
    )
)

echo.
echo 完了: %COUNT% ファイルをコピーしました
endlocal

実践例B: 各フォルダにログファイルを自動生成する

@echo off
setlocal

rem 現在時刻を取得
set NOW=%DATE% %TIME%

for /d %%F in (*) do (
    rem 各フォルダに processed.log を作成・追記
    (
        echo ===========================
        echo 処理日時: %NOW%
        echo フォルダ: %%~fF
        echo ファイル数:
        dir /b /a-d "%%F*" 2>nul | find /c /v ""
        echo ===========================
    ) >> "%%Fprocessed.log"
    echo ログ作成: %%Fprocessed.log
)

endlocal

実践例C: 特定パターンのフォルダだけ処理する

フォルダ名に「backup_」が含まれるフォルダだけ処理する例です。

@echo off
setlocal enabledelayedexpansion

for /d %%F in (*) do (
    set FNAME=%%~nxF

    rem フォルダ名に "backup_" が含まれるか確認
    echo !FNAME! | findstr /i "backup_" >nul
    if not errorlevel 1 (
        echo 対象フォルダ: %%F
        rem ここに処理を記述
    ) else (
        echo スキップ: %%F
    )
)

endlocal

ワイルドカードで絞り込む方法(シンプル)

@echo off
setlocal

rem "backup_" で始まるフォルダだけに for /d のパターンを適用
for /d %%F in (backup_*) do (
    echo バックアップ対象: %%F
    rem 処理をここに記述
)

endlocal
ワイルドカード絞り込みが使える場合はこちらが簡単
for /d %%F in (backup_*) do のようにパターンに直接ワイルドカードを使えば、iffindstr なしで目的のフォルダだけをループできます。
前後一致・部分一致などが必要なケースは findstrif 条件との組み合わせを使いましょう。

実践例D: フォルダごとのファイル数・合計サイズをCSVに出力

@echo off
setlocal enabledelayedexpansion

set CSVFILE=%~dp0folder_summary.csv

rem ヘッダー
echo フォルダ名,ファイル数,合計サイズ(bytes) > "%CSVFILE%"

for /d %%F in (*) do (
    set FCOUNT=0
    set FSIZE=0

    for %%G in ("%%F*") do (
        set /a FCOUNT+=1
        set /a FSIZE+=%%~zG
    )

    echo %%~nxF,!FCOUNT!,!FSIZE! >> "%CSVFILE%"
    echo  %%~nxF: !FCOUNT! files  !FSIZE! bytes
)

echo.
echo CSV 出力完了: %CSVFILE%
endlocal

実践例E: ROBOCOPYで各フォルダをバックアップ

@echo off
setlocal enabledelayedexpansion

set BACKUP_ROOT=D:ackup\%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%

for /d %%F in ("C:work*") do (
    set SRC=%%~fF
    set DST=%BACKUP_ROOT%\%%~nxF

    echo バックアップ中: !SRC! → !DST!
    robocopy "!SRC!" "!DST!" /mir /r:2 /w:5 /log+:"%BACKUP_ROOT%
obocopy_%%~nxF.log" >nul
    if errorlevel 8 (
        echo [ERROR] %%~nxF のバックアップでエラーが発生しました
    ) else (
        echo [OK]    %%~nxF 完了
    )
)

endlocal

ROBOCOPY の詳しい使い方は ROBOCOPYの使い方完全ガイド(ミラーリング・差分コピー・ログ出力) を参照してください。

よくある落とし穴

落とし穴1: パスにスペースがあるとフォルダが分断される

rem NG: パスにスペースがあると for /d が正しく動作しない
for /d %%F in (C:workmy projects*) do (
    echo %%F
)
rem → "C:workmy" と "projects*" に分断される

rem OK: ダブルクォートで囲む
for /d %%F in ("C:workmy projects*") do (
    echo %%~fF
)

落とし穴2: ネストしたFOR内で変数が更新されない

rem NG: enabledelayedexpansion なしでは COUNT が更新されない
@echo off
setlocal
set COUNT=0
for /d %%F in (*) do (
    for %%G in ("%%F*.txt") do (
        set /a COUNT+=1
    )
)
echo %COUNT%   ← 常に 0 になる!

rem OK: enabledelayedexpansion を使い !COUNT! で参照
@echo off
setlocal enabledelayedexpansion
set COUNT=0
for /d %%F in (*) do (
    for %%G in ("%%F*.txt") do (
        set /a COUNT+=1
    )
)
echo !COUNT!

落とし穴3: for /r の結果に “.” が含まれる

rem for /r %%F in (.) は各フォルダの "." を対象にする
rem %%F の値例: C:worksub1.  C:worksub2.

for /r "C:work" %%F in (.) do (
    rem %%~dpF = "C:worksub1" (末尾  付きのディレクトリ)
    echo %%~dpF
)

rem フォルダ名のみ(末尾  なし)が必要な場合は以下
setlocal enabledelayedexpansion
for /r "C:work" %%F in (.) do (
    set D=%%~dpF
    set D=!D:~0,-1!   ← 末尾の  を除去
    echo !D!
)

落とし穴4: for /d は空フォルダもマッチする

rem for /d はファイルが入っていない空フォルダもループ対象になる
rem ファイルがあるフォルダだけ処理したい場合は dir で確認

for /d %%F in (*) do (
    rem フォルダが空かどうかチェック
    dir /b /a-d "%%F*" >nul 2>&1
    if not errorlevel 1 (
        echo ファイルあり: %%F
    ) else (
        echo 空フォルダ(スキップ): %%F
    )
)

落とし穴5: FORループ内でGOTOを使うとループが中断される

rem NG: for ループ内で goto するとループが終了してしまう
for /d %%F in (*) do (
    if exist "%%Fimportant.txt" goto :found
)

rem OK: 条件分岐はループ内に収める・またはCALLでサブルーチン化
for /d %%F in (*) do (
    call :process "%%F"
)
goto :end

:process
set FOLDER=%~1
if exist "%FOLDER%important.txt" (
    echo 重要ファイルあり: %FOLDER%
)
goto :eof

:end

FOR ループ内での GOTO の制約については バッチファイルのラベル・GOTO・CALL 完全ガイド で詳しく解説しています。

よくある質問(FAQ)

Q 特定のフォルダ名を除外してループしたい
A

if で除外条件を追加します。複数フォルダを除外する場合は if %%~nxF neq を連ねるか findstr で否定します。

@echo off
setlocal

rem "temp" と "logs" フォルダを除外してループ
for /d %%F in (*) do (
    if /i "%%~nxF" neq "temp" (
        if /i "%%~nxF" neq "logs" (
            echo 処理中: %%F
        )
    )
)

endlocal
Q ループ処理をサブルーチンに切り出したい
A

call :ラベル名 "%%F" でサブルーチンに引数としてフォルダパスを渡せます。処理が長い場合やネストが深くなる場合に有効です。

@echo off
setlocal

for /d %%F in (*) do (
    call :process_folder "%%~fF"
)
goto :end

:process_folder
setlocal
set FOLDER=%~1
echo === 処理中: %FOLDER% ===
for %%G in ("%FOLDER%*.txt") do (
    echo   テキスト: %%~nxG
)
endlocal
goto :eof

:end
echo すべてのフォルダの処理が完了しました

CALL を使ったサブルーチン化の詳細は バッチファイルのサブルーチン完全ガイド を参照してください。

Q フォルダ数をカウントしてから処理を始めたい
A

先に数を数えてから処理するには2パスで行います。

@echo off
setlocal enabledelayedexpansion

rem まずフォルダ数をカウント
set TOTAL=0
for /d %%F in (*) do set /a TOTAL+=1

echo 対象フォルダ数: %TOTAL%

rem 処理パス
set CURRENT=0
for /d %%F in (*) do (
    set /a CURRENT+=1
    echo [!CURRENT!/%TOTAL%] 処理中: %%F
    rem 処理をここに記述
)

endlocal
Q 日付順・名前順でフォルダを処理したい
A

for /d はファイルシステムの順序(通常はアルファベット順)でループします。日付順など特定の順序が必要な場合は、dir /o:d /b /ad の出力を for /f で受け取る方法が確実です。

@echo off
setlocal

rem 日付順(古い順)にサブフォルダをループ
for /f "delims=" %%F in ('dir /b /ad /o:d') do (
    echo %%F
)

rem 名前の逆順(Z→A)
for /f "delims=" %%F in ('dir /b /ad /o:-n') do (
    echo %%F
)

endlocal
Q 再帰処理で深すぎる階層にはアクセスしたくない
A

深さを制限するにはサブルーチンで「深さカウンタ」を管理します。

@echo off
setlocal

set MAX_DEPTH=2
call :scan "C:work" 0

goto :end

:scan
setlocal
set DIR=%~1
set DEPTH=%~2

if %DEPTH% GEQ %MAX_DEPTH% goto :eof

echo %DIR%

set /a NEXT_DEPTH=%DEPTH%+1
for /d %%F in ("%DIR%*") do (
    call :scan "%%~fF" %NEXT_DEPTH%
)
endlocal
goto :eof

:end

まとめ

目的 推奨構文
カレントの直下サブフォルダを処理 for /d %%F in (*) do
指定パスの直下サブフォルダを処理 for /d %%F in ("C:path*") do
全階層のサブフォルダを再帰処理 for /r "C:path" %%F in (.) do
全階層の特定ファイルを再帰処理 for /r "C:path" %%F in (*.ext) do
固定フォルダリストをループ for %%F in ("A" "B" "C") do
テキストファイルのフォルダ一覧をループ for /f "usebackq delims=" %%F in ("list.txt") do
フォルダ→ファイルのネストループ for /d %%F in (*) do ( for %%G in ("%%F*") do )
特定パターンのフォルダだけ処理 for /d %%F in (prefix_*) do(ワイルドカード絞り込み)
ループ内で変数更新が必要 setlocal enabledelayedexpansion + !VAR! で参照

FOR 文の構文全般については FOR文の使い方完全ガイド(ファイル・数値・文字列ループを網羅) を、フォルダ一覧の取得については バッチファイルで指定フォルダ配下のフォルダ一覧を取得する方法 も合わせて参照してください。