バッチファイルでフォルダ一覧を取得したい場面は多くあります。バックアップ対象の確認・古いフォルダの検出・フォルダ構造の記録など、様々な自動化タスクの基盤になるテクニックです。
この記事では バッチファイルで指定フォルダ配下のフォルダ一覧を取得する全手法 を体系的に解説します。基本の1行コマンドから「再帰的な全サブフォルダ取得」「条件フィルタ」「CSV出力」まで網羅します。
- for /d でサブフォルダ一覧を取得する方法(基本)
- dir /ad /b コマンドとの使い分け
- サブフォルダを再帰的にすべて取得する方法(for /r)
- フォルダ名パターンでフィルタする方法
- フォルダ一覧をCSV・テキストファイルに出力する方法
- フォルダ数をカウントする方法
- フォルダの作成日時・更新日時を取得する方法
- 落とし穴5選・実践例3本・FAQ6問
1. 基本:for /d でサブフォルダ一覧を取得
for /d はフォルダ(ディレクトリ)専用のループ構文です。指定したパスの直下にあるフォルダのみを対象にします(サブフォルダの中までは入りません)。
1-1. 最もシンプルな書き方
@echo off
setlocal enabledelayedexpansion
set "TARGET=C:\work"
:: for /d: 指定パス直下のフォルダ(ディレクトリ)のみをループ
for /d %%D in ("%TARGET%\*") do (
echo %%~nxD
)
endlocal
%%~nxD はフォルダ名のみ(パスなし)を取得します。%%D だとフルパスになります。変数修飾子の詳細は ファイルの拡張子一括変換ガイド のfor修飾子テーブルも参照してください。
1-2. フォルダの存在チェック付き(エラーハンドリング)
@echo off
setlocal enabledelayedexpansion
set "TARGET=C:\work"
:: 対象フォルダの存在確認
if not exist "%TARGET%\" (
echo [ERROR] フォルダが見つかりません: %TARGET%
exit /b 1
)
set "COUNT=0"
for /d %%D in ("%TARGET%\*") do (
echo %%~nxD
set /a COUNT+=1
)
if !COUNT! equ 0 (
echo サブフォルダが見つかりませんでした
) else (
echo.
echo 合計: !COUNT! フォルダ
)
endlocal
2. dir /ad /b でフォルダ一覧を取得
dir コマンドに /ad(ディレクトリのみ)と /b(シンプル表示)を組み合わせると、フォルダ名だけのリストが得られます。
2-1. dir コマンドの基本
:: /a:d または /ad : ディレクトリ(フォルダ)のみ表示 :: /b : ファイル名のみ(余分な情報なし) dir /ad /b "C:\work" :: 出力例: :: backup :: logs :: temp :: work2024
2-2. dir 出力を for /f でループ処理
@echo off
setlocal enabledelayedexpansion
set "TARGET=C:\work"
for /f "delims=" %%D in ('dir /ad /b "%TARGET%"') do (
echo フォルダ名: %%D
)
endlocal
コマンドプロンプトで直接実行するとき、バッチファイルでは
for /f ... in ('dir ...') の形で使います。フォルダが1件もない場合は dir がエラーを返すため、2>nul でエラーを抑制するか if exist で事前チェックしてください。
3. for /d と dir /ad /b の使い分け
| 項目 | for /d | dir /ad /b |
|---|---|---|
| 構文のシンプルさ | ループ内で直接処理できる | for /f と組み合わせが必要 |
| フルパス取得 | %%D または %%~fD |
dir /ad /b /s でフルパス |
| 再帰(全サブフォルダ) | for /r か dir /s が必要 | /s オプションで可能 |
| ソート | ファイルシステム順(固定) | /o:n 等でソート指定可能 |
| フィルタ(名前パターン) | ワイルドカード使用可 | ワイルドカード使用可 |
| ファイルに出力 | リダイレクトで出力 | > でそのまま出力 |
| おすすめ用途 | ループ処理・変数操作 | 一覧表示・ファイル出力・再帰 |
4. サブフォルダを再帰的にすべて取得
直下のサブフォルダだけでなく、孫・ひ孫フォルダも含めてすべてリストアップするパターンです。
4-1. dir /ad /b /s(最もシンプルな再帰)
:: /s : サブフォルダも再帰的に検索
dir /ad /b /s "C:\work"
:: フルパスでリストアップ
dir /ad /b /s "C:\work" 2>nul
:: for /f でループ処理(フルパスで取得)
@echo off
for /f "delims=" %%D in ('dir /ad /b /s "C:\work" 2^>nul') do (
echo %%D
)
4-2. for /r で再帰的にフォルダをループ
@echo off
setlocal enabledelayedexpansion
set "TARGET=C:\work"
set "COUNT=0"
:: for /r %%D in (.) : 各サブフォルダに .(カレント) を探索 → フォルダパスを取得
for /r "%TARGET%" %%D in (.) do (
:: %%~dpD : ドライブ+パス部分(末尾に\が付く)
:: 対象フォルダ自体は除外
if /i not "%%~dpD" == "%TARGET%\" (
echo %%~dpD
set /a COUNT+=1
)
)
echo 合計: !COUNT! フォルダ
endlocal
for /r "C:\work" %%D in (.) は、C:\work 以下のすべてのフォルダに対して .(カレントディレクトリ参照)を当てはめます。%%~dpD でフォルダのドライブ+パスが取得できます。dir /ad /b /s と組み合わせた for /f 方式の方が直感的なため、再帰処理では dir /s 方式を推奨します。
5. フォルダ名パターンでフィルタ
特定の文字列を含むフォルダだけを取得するパターンです。
5-1. for /d のワイルドカードでフィルタ
@echo off
:: "backup" で始まるフォルダのみ取得
for /d %%D in ("C:\work\backup*") do (
echo %%~nxD
)
:: "2024" を含むフォルダ(ワイルドカードで前後を指定)
for /d %%D in ("C:\work\*2024*") do (
echo %%~nxD
)
5-2. findstr でフィルタ(より柔軟)
@echo off
setlocal enabledelayedexpansion
:: dir + findstr で "log" を含むフォルダのみ表示
for /f "delims=" %%D in ('dir /ad /b "C:\work" ^| findstr /i "log"') do (
echo %%D
)
:: 再帰的 + フィルタ("2024" を含むフォルダ)
for /f "delims=" %%D in ('dir /ad /b /s "C:\work" ^| findstr /i "2024"') do (
echo %%D
)
endlocal
条件分岐・フィルタ処理の詳細は バッチファイルで条件分岐する方法完全ガイド を参照してください。
6. フォルダ一覧をCSV・テキストファイルに出力
フォルダ一覧を記録・共有するためにファイルに出力するパターンです。
6-1. テキストファイルに出力(シンプル)
:: カレントフォルダのサブフォルダ一覧をテキストファイルに保存 dir /ad /b "C:\work" > "C:\output\folder_list.txt" :: 再帰的(全サブフォルダ)をテキストファイルに保存 dir /ad /b /s "C:\work" > "C:\output\folder_list_all.txt" :: 追記モード(既存ファイルに追加) dir /ad /b "C:\work" >> "C:\output\folder_list.txt"
6-2. CSV形式で出力(番号・フォルダ名・フルパス)
@echo off
setlocal enabledelayedexpansion
set "TARGET=C:\work"
set "OUT=C:\output\folder_list.csv"
set "NUM=0"
:: ヘッダー行
echo No,フォルダ名,フルパス > "%OUT%"
for /d %%D in ("%TARGET%\*") do (
set /a NUM+=1
echo !NUM!,%%~nxD,%%~fD >> "%OUT%"
)
echo [OK] CSV出力完了: %OUT% (!NUM! 件)
endlocal
6-3. 日付付きファイル名で出力(タイムスタンプログ)
@echo off
setlocal enabledelayedexpansion
set "TARGET=C:\work"
:: 今日の日付を取得(YYYYMMDD形式)
for /f "tokens=2 delims==." %%D in ('wmic os get LocalDateTime /value ^| findstr LocalDateTime') do set "DT=%%D"
set "TODAY=%DT:~0,8%"
set "OUT=C:\logs\folder_list_%TODAY%.txt"
dir /ad /b "%TARGET%" > "%OUT%"
echo 出力完了: %OUT%
endlocal
日付をファイル名に使う方法の詳細は 日付と時間をファイル名に挿入する方法完全ガイド を参照してください。
7. フォルダ数をカウント
サブフォルダの件数を取得するパターンです。処理前の確認や、変化を検知する監視スクリプトに使えます。
7-1. find /c でカウント(シンプル)
:: dir /ad /b の出力行数 = フォルダ数 dir /ad /b "C:\work" 2>nul | find /c /v "" :: 再帰(全サブフォルダ数) dir /ad /b /s "C:\work" 2>nul | find /c /v ""
7-2. for /d でカウント(変数に格納)
@echo off
setlocal enabledelayedexpansion
set "TARGET=C:\work"
set "COUNT=0"
for /d %%D in ("%TARGET%\*") do (
set /a COUNT+=1
)
echo サブフォルダ数: !COUNT!
endlocal
7-3. フォルダ数を条件で使う(閾値チェック)
@echo off
setlocal enabledelayedexpansion
set "TARGET=C:\logs"
set "MAX=100"
set "COUNT=0"
for /d %%D in ("%TARGET%\*") do set /a COUNT+=1
if !COUNT! gtr %MAX% (
echo [WARN] フォルダが %MAX% 件を超えています: !COUNT! 件
) else (
echo [OK] フォルダ数: !COUNT! 件 / 上限 %MAX% 件
)
endlocal
8. フォルダの作成日時・更新日時を取得
フォルダのタイムスタンプを取得することで、古いフォルダの検出や更新履歴の記録が可能になります。
8-1. forfiles でフォルダ情報を取得
@echo off :: @ISDIR==TRUE : フォルダのみ処理(ファイルはスキップ) :: @FILE: 名前, @FDATE: 更新日付, @FTIME: 更新時刻 forfiles /p "C:\work" /c "cmd /c if @ISDIR==TRUE echo @FILE @FDATE @FTIME" :: 30日以上更新されていないフォルダを検出 forfiles /p "C:\work" /d -30 /c "cmd /c if @ISDIR==TRUE echo 古いフォルダ: @FILE"
8-2. dir /ad でタイムスタンプ付きリスト
:: /ad: フォルダのみ, /tc: 作成日時でソート表示 dir /ad /tc "C:\work" :: /o-d: 更新日時の新しい順でソート dir /ad /o-d "C:\work" :: 更新日時の古い順(最も古いフォルダが先頭) dir /ad /od "C:\work"
forfiles /d -30 は「30日以上前に更新されたファイル/フォルダ」を対象にします。/d +0 は今日以降に更新されたもの、/d 2024/01/01 のように日付指定も可能です。
9. 落とし穴5選と対策
落とし穴1:for /d は直下1階層のみ。サブフォルダの中には入らない
:: NG: for /d はネストしたフォルダを取得しない
:: C:\work\ 直下のフォルダしかリストアップされない
for /d %%D in ("C:\work\*") do echo %%D
:: OK: 再帰的に取得したい場合は dir /s か for /r を使う
for /f "delims=" %%D in ('dir /ad /b /s "C:\work" 2^>nul') do echo %%D
落とし穴2:フォルダ名にスペースが含まれると for /d が分割されて失敗する
:: NG: スペースを含むフォルダ名が分割される
for /d %%D in (C:\work\*) do echo %%D
:: OK: 必ずダブルクォートで囲む
for /d %%D in ("C:\work\*") do echo %%D
:: NG: 変数を展開するとき引用符を忘れる
set "TARGET=C:\my work"
for /d %%D in (%TARGET%\*) do echo %%D :: スペースで分割される
:: OK: 変数展開も引用符で囲む
for /d %%D in ("%TARGET%\*") do echo %%D
落とし穴3:dir /b なしで使うと . と .. がリストに混入する
:: NG: /b なしの dir はヘッダー行・. と .. もリストアップされる
for /f "delims=" %%D in ('dir /ad "C:\work"') do echo %%D
:: → ヘッダー/フッター行・. (カレント)・.. (親) も処理対象に入ってしまう
:: OK: /b オプションを付けることで . と .. は表示されず純粋なフォルダ名のみになる
for /f "delims=" %%D in ('dir /ad /b "C:\work" 2^>nul') do echo %%D
落とし穴4:for /f で dir の出力をパイプするとき ^ でエスケープが必要
:: NG: パイプ | をエスケープしないと for /f がコマンドとして解釈する
for /f "delims=" %%D in ('dir /ad /b "C:\work" | findstr "log"') do echo %%D
:: OK: バッチファイル内では ^ でエスケープ
for /f "delims=" %%D in ('dir /ad /b "C:\work" ^| findstr "log"') do echo %%D
:: OK: > も同様にエスケープが必要
for /f "delims=" %%D in ('dir /ad /b /s "C:\work" 2^>nul') do echo %%D
落とし穴5:対象フォルダが存在しないと dir がエラーを返してスクリプトが止まる
:: NG: フォルダが存在しない場合にエラーが表示される
for /d %%D in ("C:\nonexistent\*") do echo %%D
:: OK: 事前に存在チェックを行う
set "TARGET=C:\work"
if not exist "%TARGET%\" (
echo [ERROR] フォルダが存在しません: %TARGET%
exit /b 1
)
for /d %%D in ("%TARGET%\*") do echo %%~nxD
:: OK: 2>nul でエラーを抑制する方法(エラーチェックなし)
dir /ad /b "C:\work" 2>nul
エラーハンドリングの詳細は ERRORLEVELを使ったエラーハンドリング完全ガイド を参照してください。
10. 実践例3本
実践例1:バックアップ対象フォルダを自動検出してrobocopyで一括コピー
指定フォルダ直下のサブフォルダを全てバックアップするパターンです。新しく作られたフォルダも自動的にバックアップ対象になります。
@echo off
setlocal enabledelayedexpansion
set "SRC_ROOT=C:\projects"
set "DST_ROOT=D:\backup\projects"
set "LOG=C:\logs\backup_log.txt"
set "COUNT=0"
if not exist "%DST_ROOT%" mkdir "%DST_ROOT%"
:: src_root 直下のサブフォルダを1つずつバックアップ
for /d %%D in ("%SRC_ROOT%\*") do (
echo [START] %%~nxD のバックアップ中...
robocopy "%%D" "%DST_ROOT%\%%~nxD" /MIR /R:2 /W:5 /LOG+:"%LOG%" /NP /NDL
if !ERRORLEVEL! leq 3 (
echo [OK] %%~nxD 完了
set /a COUNT+=1
) else (
echo [ERROR] %%~nxD でエラー発生 (ERRORLEVEL=!ERRORLEVEL!)
)
)
echo ===== バックアップ完了: !COUNT! フォルダ =====
endlocal
robocopy は正常終了でも 0・1・2・3 を返します(コピーしたファイルの数によって変わる)。4 以上がエラーのため
if !ERRORLEVEL! leq 3 で判定します。
フォルダコピーの詳細は バッチファイルでフォルダをコピーする方法完全ガイド を参照してください。
実践例2:古いフォルダを検出してCSVに記録
指定日数以上更新のないフォルダを検出してCSVに記録するパターンです。定期メンテナンスや容量管理に役立ちます。
@echo off
setlocal enabledelayedexpansion
set "TARGET=C:\archive"
set "DAYS=90"
set "OUT=C:\reports\old_folders.csv"
:: 今日の日付を取得
for /f "tokens=2 delims==." %%D in ('wmic os get LocalDateTime /value ^| findstr LocalDateTime') do set "DT=%%D"
set "TODAY=%DT:~0,8%"
echo 日付,フォルダ名,フルパス > "%OUT%"
set "FOUND=0"
forfiles /p "%TARGET%" /d -%DAYS% /c "cmd /c if @ISDIR==TRUE echo %TODAY%,@FILE,@PATH" >> "%OUT%" 2>nul
:: 件数をカウント
for /f %%N in ('find /c /v "" "%OUT%"') do set "LINES=%%N"
set /a FOUND=!LINES! - 1
echo [OK] %DAYS%日以上未更新フォルダ: !FOUND! 件 -> %OUT%
endlocal
実践例3:2つのフォルダ構造を比較して差分を出力
本番フォルダとバックアップフォルダのサブフォルダ構成を比較し、片方にしかないフォルダを検出するパターンです。
@echo off
setlocal enabledelayedexpansion
set "FOLDER_A=C:\production"
set "FOLDER_B=D:\backup\production"
set "TMPFILE=%TEMP%\folders_a_%RANDOM%.tmp"
set "DIFF_COUNT=0"
:: Aのフォルダ一覧を一時ファイルに保存
dir /ad /b "%FOLDER_A%" 2>nul > "%TMPFILE%"
:: Aにあって B にないフォルダを検出
echo ===== A にあって B にないフォルダ =====
:: usebackq: ファイル名にスペースが含まれても正しく読み込む
for /f "usebackq delims=" %%D in ("%TMPFILE%") do (
if not exist "%FOLDER_B%\%%D" (
echo [ONLY_A] %%D
set /a DIFF_COUNT+=1
)
)
:: B にあって A にないフォルダを検出
echo ===== B にあって A にないフォルダ =====
for /d %%D in ("%FOLDER_B%\*") do (
if not exist "%FOLDER_A%\%%~nxD" (
echo [ONLY_B] %%~nxD
set /a DIFF_COUNT+=1
)
)
del "%TMPFILE%" 2>nul
if !DIFF_COUNT! equ 0 (
echo [OK] フォルダ構造は一致しています
) else (
echo [WARN] 差分: !DIFF_COUNT! フォルダ
)
endlocal
フォルダの一括ループ処理の詳細は 複数フォルダをループして一括処理する方法完全ガイド を参照してください。
まとめ:使い分け早見表
| やりたいこと | コマンド | ポイント |
|---|---|---|
| 直下のサブフォルダをループ | for /d %%D in ("パス\*") do |
ループ処理・変数操作向き |
| サブフォルダ名だけ一覧表示 | dir /ad /b "パス" |
シンプル・ファイル出力向き |
| 再帰的に全サブフォルダ取得 | dir /ad /b /s "パス" |
全階層を一括取得 |
| for /f で再帰ループ | for /f ... in ('dir /ad /b /s ...') |
各フォルダでバッチ処理 |
| 名前パターンでフィルタ | for /d %%D in ("パス\*keyword*") |
ワイルドカード使用可 |
| CSV出力 | for /d + echo No,名前,パス >> CSV | setlocal enabledelayedexpansion 必須 |
| フォルダ数カウント | dir /ad /b 2>nul | find /c /v "" |
1行で取得可能 |
| 古いフォルダ検出 | forfiles /d -日数 /c "if @ISDIR==TRUE ..." |
日数・日付で柔軟フィルタ |
setlocal enabledelayedexpansion 完全ガイド も合わせて参照してください。
FAQ
* を忘れている("C:\work\" ではなく "C:\work\*")、②対象フォルダ直下にサブフォルダが存在しない。dir /ad "C:\work" でサブフォルダの有無を確認してください。for /d %%D in ("%TARGET%\*") do のように、パターンをダブルクォートで囲んでください。また %%D を使う箇所でも "%%D" と引用符をつけると安全です。dir /ad /b /s "C:\work" または for /f "delims=" %%D in ('dir /ad /b /s "C:\work" 2^>nul') を使います。for /d は直下1階層のみです。dir /ad /b "C:\work" > "output.txt" でシンプルに出力できます。追記は >>、ファイル名に日付を付けるには %DATE:~0,4%%DATE:~5,2%%DATE:~8,2% または wmic で取得した日付文字列を使います。| や リダイレクト > を for /f のコマンド部分で使うときは ^ でエスケープが必要です。例: 'dir /ad /b "C:\work" ^| findstr "log"'dir /a:dh /b "C:\work"(隠しフォルダのみ)または dir /a:d /b "C:\work" では通常フォルダのみです。隠し属性も含む全フォルダは attrib /d /s "C:\work\*" | findstr "D" や PowerShell の Get-ChildItem -Force -Directory の方が確実です。