【bat】バッチファイルで複数のCSVファイルを一つにまとめる方法完全ガイド|ヘッダー制御・サブフォルダ再帰・出力除外・文字コード・PowerShell・実践パターンまで

複数の CSV ファイルを1つにまとめる作業は、月次レポートの集計・ログ統合・データ前処理などでよく発生します。バッチファイルで自動化すれば、毎月の手作業を数秒で済ませることができます。

この記事では CSV 結合の全パターン を体系的に解説します。単純な全行結合から「ヘッダーを1回だけ保持する」「サブフォルダも再帰検索する」「出力ファイル自身を除外する」「ファイル名を列として追加する」といった実務的なパターンまで網羅します。

この記事でわかること

  • copy コマンドで最速結合する方法(ヘッダーあり)
  • ヘッダーを1行だけ保持して結合する方法
  • サブフォルダを再帰的に検索して結合する方法(for /r)
  • 出力ファイル自身が結合対象に混入しないようにする方法
  • 結合元ファイル名を列として追加する方法(トレーサビリティ)
  • 文字コード(Shift-JIS・UTF-8)混在への対処
  • 重複行の除去(sort /unique)
  • PowerShell との連携で複雑な結合を処理する方法
  • 落とし穴5選・実践例3本・FAQ6問
スポンサーリンク
  1. 1. 最速結合:copy コマンドでヘッダーごと全行を結合
  2. 2. ヘッダーを1行だけ保持して結合する(最重要パターン)
    1. 2-1. 基本実装
    2. 2-2. 処理件数をカウントしながら結合
  3. 3. サブフォルダを再帰的に検索して結合(for /r)
  4. 4. 特定フォルダのみ・ファイル名パターンで絞り込んで結合
  5. 5. 結合元ファイル名を列として追加する(トレーサビリティ)
  6. 6. 出力ファイルの末尾に余計な改行が入る問題への対処
  7. 7. 文字コード(Shift-JIS・UTF-8)混在への対処
    1. 7-1. すべてのファイルが同じ文字コード(Shift-JIS)の場合
    2. 7-2. UTF-8 のCSVを結合する(PowerShell使用)
  8. 8. 重複行を除去しながら結合(sort /unique)
  9. 9. 差分結合(既存の combined.csv に新しいファイルだけ追記する)
  10. 10. 落とし穴5選と対策
    1. 落とし穴1:出力ファイル自身が結合対象に含まれる
    2. 落とし穴2:echo でリダイレクトすると行末に余分なスペースが入る
    3. 落とし穴3:ループ内でフラグを更新すると %変数% では古い値になる
    4. 落とし穴4:for /f が ; 始まりの行をスキップする(eol の問題)
    5. 落とし穴5:for /r は出力ファイルのフルパスと比較しないと除外できない
  11. 11. 実践例3本
    1. 実践例1:月次売上CSVを四半期ファイルに結合する
    2. 実践例2:複数システムのログCSVをサブフォルダから再帰収集して統合する
    3. 実践例3:PowerShell で UTF-8 CSV を結合して重複除去・ソートまで行う
  12. 12. まとめ:CSV結合方法の使い分け表
  13. FAQ

1. 最速結合:copy コマンドでヘッダーごと全行を結合

最もシンプルな方法です。すべてのファイルのヘッダーも含めて全行を連結します。ヘッダーが重複しても問題なく、後でフィルタリングする用途に向いています。

@echo off
:: 同一フォルダ内のすべての CSV を combined.csv に結合
copy *.csv combined.csv
echo [OK] combined.csv に結合しました

copy コマンドはバイナリコピーではなく、*.csv のようなワイルドカードを使うとテキストとして連結されます(改行も保持されます)。ただし各ファイルのヘッダーがすべて含まれるため、ヘッダーを1行にしたい場合は次節の方法を使ってください。

:: copy コマンドの注意点
:: - combined.csv が *.csv に含まれる場合、自己参照のループが起きることがある
::   → 出力ファイルを別フォルダに指定するか、拡張子を変えて回避
copy *.csv D:outputcombined.csv

:: + 演算子で明示的に列挙する方法
copy jan.csv + feb.csv + mar.csv Q1.csv

リダイレクト(>/>>)の詳細は リダイレクトの使い方完全ガイド も参照してください。

2. ヘッダーを1行だけ保持して結合する(最重要パターン)

実務で最もよく使うパターンです。1ファイル目のヘッダーのみを出力し、2ファイル目以降はヘッダー行をスキップして結合します。

2-1. 基本実装

@echo off
setlocal enabledelayedexpansion

set "OUT=combined.csv"
set "FIRST=1"

:: 出力ファイルを初期化
if exist "%OUT%" del "%OUT%"

for %%F in (*.csv) do (
    :: 出力ファイル自身をスキップ
    if /i not "%%F"=="%OUT%" (
        if "!FIRST!"=="1" (
            :: 1ファイル目: ヘッダーを含めすべての行を出力
            type "%%F" >> "%OUT%"
            set "FIRST=0"
        ) else (
            :: 2ファイル目以降: ヘッダー(1行目)をスキップして出力
            for /f "usebackq skip=1 tokens=* delims=" %%L in ("%%F") do (
                echo %%L >> "%OUT%"
            )
        )
    )
)

echo [OK] 結合完了 → %OUT%

2-2. 処理件数をカウントしながら結合

@echo off
setlocal enabledelayedexpansion

set "OUT=combined.csv"
set "FIRST=1"
set "FILE_COUNT=0"
set "ROW_COUNT=0"

if exist "%OUT%" del "%OUT%"

for %%F in (*.csv) do (
    if /i not "%%F"=="%OUT%" (
        set /a FILE_COUNT+=1
        if "!FIRST!"=="1" (
            type "%%F" >> "%OUT%"
            set "FIRST=0"
        ) else (
            for /f "usebackq skip=1 tokens=* delims=" %%L in ("%%F") do (
                echo %%L >> "%OUT%"
                set /a ROW_COUNT+=1
            )
        )
    )
)

:: 総行数取得(ヘッダー含む)
set "TOTAL=0"
for /f %%C in ('find /c /v "" ^< "%OUT%"') do set "TOTAL=%%C"

echo [OK] !FILE_COUNT! ファイルを結合しました
echo      出力行数(ヘッダー含む): !TOTAL! 行 → %OUT%

遅延展開が必要な理由は setlocal enabledelayedexpansion 完全ガイド を参照してください。

3. サブフォルダを再帰的に検索して結合(for /r)

複数のサブフォルダに分散した CSV をまとめて1ファイルに結合するパターンです。FOR文の使い方完全ガイド も参照してください。

@echo off
setlocal enabledelayedexpansion

set "SRC_DIR=C:workcsv"
set "OUT=C:workcombined_all.csv"
set "FIRST=1"
set "FILE_COUNT=0"

if exist "%OUT%" del "%OUT%"

:: for /r でサブフォルダを再帰検索
for /r "%SRC_DIR%" %%F in (*.csv) do (
    :: 出力ファイル自身をスキップ
    if /i not "%%~fF"=="%OUT%" (
        set /a FILE_COUNT+=1
        echo 処理中: %%~nxF (%%~dpF)
        if "!FIRST!"=="1" (
            type "%%F" >> "%OUT%"
            set "FIRST=0"
        ) else (
            for /f "usebackq skip=1 tokens=* delims=" %%L in ("%%F") do (
                echo %%L >> "%OUT%"
            )
        )
    )
)

if "!FIRST!"=="1" (
    echo [WARN] CSV ファイルが見つかりませんでした: %SRC_DIR%
    exit /b 1
)
echo [OK] !FILE_COUNT! 件のCSVを結合しました → %OUT%

4. 特定フォルダのみ・ファイル名パターンで絞り込んで結合

「2024年のファイルだけ」「sales_で始まるファイルだけ」のように結合対象を絞り込むパターンです。

@echo off
setlocal enabledelayedexpansion

set "OUT=sales_combined.csv"
set "FIRST=1"

if exist "%OUT%" del "%OUT%"

:: "sales_" で始まる CSV だけを結合
for %%F in (sales_*.csv) do (
    if /i not "%%F"=="%OUT%" (
        if "!FIRST!"=="1" (
            type "%%F" >> "%OUT%"
            set "FIRST=0"
        ) else (
            for /f "usebackq skip=1 tokens=* delims=" %%L in ("%%F") do (
                echo %%L >> "%OUT%"
            )
        )
    )
)

:: 複数のパターンをORで結合(2024_ または 2025_ で始まるファイル)
for %%P in (2024_*.csv 2025_*.csv) do (
    if /i not "%%P"=="%OUT%" echo 対象: %%P
)

5. 結合元ファイル名を列として追加する(トレーサビリティ)

どの元ファイルのデータかを追跡できるよう、各行にファイル名の列を追加して結合するパターンです。

@echo off
setlocal enabledelayedexpansion

set "OUT=combined_with_source.csv"
set "FIRST=1"

if exist "%OUT%" del "%OUT%"

for %%F in (*.csv) do (
    if /i not "%%F"=="%OUT%" (
        if "!FIRST!"=="1" (
            :: ヘッダー行に source_file 列を追加
            for /f "usebackq tokens=* delims=" %%H in ("%%F") do (
                echo %%H,source_file >> "%OUT%"
                goto :header_done
            )
            :header_done
            set "FIRST=0"
        )
        :: データ行に ファイル名(拡張子なし)を追加
        for /f "usebackq skip=1 tokens=* delims=" %%L in ("%%F") do (
            echo %%L,%%~nF >> "%OUT%"
        )
    )
)

echo [OK] ファイル名列付きで結合しました → %OUT%

6. 出力ファイルの末尾に余計な改行が入る問題への対処

echo %%L >> file は各行の末尾に改行が追加されますが、ファイル末尾に余分な空行が入ることがあります。また type コマンドを使うとこの問題を回避できます。

@echo off
setlocal enabledelayedexpansion

set "OUT=combined.csv"
set "FIRST=1"

if exist "%OUT%" del "%OUT%"

for %%F in (*.csv) do (
    if /i not "%%F"=="%OUT%" (
        if "!FIRST!"=="1" (
            :: type は改行を余分に追加しないため、1ファイル目は type を使う
            type "%%F" >> "%OUT%"
            set "FIRST=0"
        ) else (
            :: 2ファイル目以降も可能な限り type + findstr でヘッダー除外
            :: skip=1 の for /f より type + findstr /v の方が高速な場合がある
            more +1 "%%F" >> "%OUT%"
        )
    )
)

echo [OK] 結合完了 → %OUT%

more +1 "%%F" は1行目をスキップしてファイル内容を出力するシンプルな方法です。ただし more コマンドは大きなファイルでは遅いため、大量データには for /f skip=1 を使いましょう。

7. 文字コード(Shift-JIS・UTF-8)混在への対処

異なる文字コードのCSVを結合すると文字化けが発生します。PowerShell に委譲するのが最も確実な解決策です。バッチでUnicodeファイルを扱えない原因と解決策 も参照してください。

7-1. すべてのファイルが同じ文字コード(Shift-JIS)の場合

:: Shift-JIS の CSV は copy コマンドや for /f でそのまま結合できる
@echo off
setlocal enabledelayedexpansion
set "OUT=combined.csv"
set "FIRST=1"
if exist "%OUT%" del "%OUT%"
for %%F in (*.csv) do (
    if /i not "%%F"=="%OUT%" (
        if "!FIRST!"=="1" ( type "%%F" >> "%OUT%" & set "FIRST=0"
        ) else ( more +1 "%%F" >> "%OUT%" )
    )
)
echo [OK] 結合完了

7-2. UTF-8 のCSVを結合する(PowerShell使用)

@echo off
:: PowerShell で UTF-8 CSV を結合(ヘッダー1行保持)
powershell -NoProfile -Command "
    $files = Get-ChildItem -Path C:workcsv -Filter *.csv | Where-Object { $_.Name -ne "combined.csv" }
    $first = $true
    foreach ($f in $files) {
        $lines = Get-Content -Path $f.FullName -Encoding UTF8
        if ($first) {
            $lines | Out-File -FilePath C:workcombined.csv -Encoding UTF8
            $first = $false
        } else {
            $lines | Select-Object -Skip 1 | Out-File -FilePath C:workcombined.csv -Encoding UTF8 -Append
        }
    }
    Write-Host "[OK] 結合完了"
"

PowerShellをバッチファイルから呼び出す方法 も参照してください。

8. 重複行を除去しながら結合(sort /unique)

複数ファイルに同一行が存在する場合(ログファイルの重複エントリなど)、重複を除去しながら結合できます。テキストファイルをソートする方法完全ガイド も参照してください。

@echo off
setlocal enabledelayedexpansion

set "OUT=combined.csv"
set "TMP=%TEMP%combined_tmp_%RANDOM%.csv"
set "FIRST=1"

if exist "%OUT%" del "%OUT%"

:: まず通常通り全ファイルを結合(ヘッダー1回保持)
for %%F in (*.csv) do (
    if /i not "%%F"=="%OUT%" (
        if "!FIRST!"=="1" ( type "%%F" >> "%OUT%" & set "FIRST=0"
        ) else ( more +1 "%%F" >> "%OUT%" )
    )
)

:: ヘッダーを保存
for /f "usebackq tokens=* delims=" %%H in ("%OUT%") do (
    set "HEADER=%%H"
    goto :got_header
)
:got_header

:: ヘッダーを除いたデータ行を sort /unique で重複除去
more +1 "%OUT%" | sort /unique > "%TMP%"

:: ヘッダー + 重複除去済みデータ を最終ファイルに書き出し
echo !HEADER! > "%OUT%"
type "%TMP%" >> "%OUT%"
del "%TMP%"

echo [OK] 重複除去済みで結合しました → %OUT%

9. 差分結合(既存の combined.csv に新しいファイルだけ追記する)

毎日新しいCSVが追加されるケースで、前回処理済みのファイルは再処理せず差分のみ追記するパターンです。

@echo off
setlocal enabledelayedexpansion

set "OUT=combined.csv"
set "PROCESSED_LOG=processed_files.txt"

:: 出力ファイルが存在しない場合は初回結合として扱う
if not exist "%OUT%" (
    set "FIRST=1"
) else (
    set "FIRST=0"
)

set "NEW_COUNT=0"

for %%F in (*.csv) do (
    if /i not "%%F"=="%OUT%" (
        :: 処理済みログに記録されているファイルはスキップ
        set "ALREADY="
        if exist "%PROCESSED_LOG%" (
            findstr /x /i /c:"%%F" "%PROCESSED_LOG%" >nul 2>&1
            if not errorlevel 1 set "ALREADY=1"
        )

        if not defined ALREADY (
            set /a NEW_COUNT+=1
            if "!FIRST!"=="1" (
                type "%%F" >> "%OUT%"
                set "FIRST=0"
            ) else (
                more +1 "%%F" >> "%OUT%"
            )
            :: 処理済みとして記録
            echo %%F >> "%PROCESSED_LOG%"
            echo [追加] %%F
        )
    )
)

if !NEW_COUNT! EQU 0 (
    echo [SKIP] 新しい CSV ファイルはありません
) else (
    echo [OK] !NEW_COUNT! ファイルを追記しました → %OUT%
)

10. 落とし穴5選と対策

落とし穴1:出力ファイル自身が結合対象に含まれる

:: NG: combined.csv が *.csv に含まれてしまい、自分自身を読み込む
for %%F in (*.csv) do (
    more +1 "%%F" >> combined.csv  :: ← combined.csv が対象に含まれる
)

:: OK: 出力ファイル自身をスキップする条件を追加
for %%F in (*.csv) do (
    if /i not "%%F"=="combined.csv" (
        more +1 "%%F" >> combined.csv
    )
)

:: または出力先を別フォルダに指定する
for %%F in (*.csv) do (
    more +1 "%%F" >> "D:outputcombined.csv"
)

落とし穴2:echo でリダイレクトすると行末に余分なスペースが入る

:: NG: echo %%L >> file は %%L の後にスペースが入ることがある
for /f "tokens=* delims=" %%L in (data.csv) do (
    echo %%L >> combined.csv
)

:: OK1: リダイレクトをループの外に出す(パフォーマンスも向上)
(
    for /f "tokens=* delims=" %%L in (data.csv) do (
        echo %%L
    )
) >> combined.csv

:: OK2: more +1 を使う(for /f より高速な場合がある)
more +1 "data.csv" >> combined.csv

変数の末尾に余計な空白が入ってしまうときの対策 も参照してください。

落とし穴3:ループ内でフラグを更新すると %変数% では古い値になる

:: NG: %FIRST% は for ループ進入時に評価されるため常に "1" になる
set "FIRST=1"
for %%F in (*.csv) do (
    if "%FIRST%"=="1" ( type "%%F" >> out.csv ) else ( more +1 "%%F" >> out.csv )
    set "FIRST=0"
)

:: OK: setlocal enabledelayedexpansion + !FIRST! を使う
setlocal enabledelayedexpansion
set "FIRST=1"
for %%F in (*.csv) do (
    if "!FIRST!"=="1" ( type "%%F" >> out.csv ) else ( more +1 "%%F" >> out.csv )
    set "FIRST=0"
)

変数展開が動かない原因と修正方法 も参照してください。

落とし穴4:for /f が ; 始まりの行をスキップする(eol の問題)

:: for /f はデフォルトで ; 始まりの行を読み飛ばす
:: CSV にセミコロンで始まるフィールドがある場合に行が欠落する

:: 対策: eol= に使わない文字を指定してスキップを無効化
for /f "usebackq eol=| skip=1 tokens=* delims=" %%L in ("data.csv") do (
    echo %%L >> combined.csv
)

落とし穴5:for /r は出力ファイルのフルパスと比較しないと除外できない

:: NG: for /r のループ変数 %%F はフルパスになる
::     "combined.csv" だけと比較してもフルパスとは一致しない
set "OUT=combined.csv"
for /r "C:work" %%F in (*.csv) do (
    if /i not "%%F"=="%OUT%" (  :: ← これでは除外できない
        more +1 "%%F" >> "%OUT%"
    )
)

:: OK: 出力ファイルのフルパスを変数に格納して比較
set "OUT_FULL=C:workcombined.csv"
for /r "C:work" %%F in (*.csv) do (
    if /i not "%%~fF"=="%OUT_FULL%" (
        more +1 "%%F" >> "%OUT_FULL%"
    )
)

11. 実践例3本

実践例1:月次売上CSVを四半期ファイルに結合する

毎月生成される売上CSVを四半期ごとのファイルに自動結合するスクリプトです。

@echo off
setlocal enabledelayedexpansion

set "BASE_DIR=C:worksales"
set "OUT_DIR=C:workquarterly"

if not exist "%OUT_DIR%" mkdir "%OUT_DIR%"

:: Q1(1〜3月)・Q2(4〜6月)・Q3(7〜9月)・Q4(10〜12月)
for %%Q in (Q1 Q2 Q3 Q4) do (
    set "OUT=%OUT_DIR%%%Q_combined.csv"
    set "FIRST=1"
    if exist "!OUT!" del "!OUT!"

    if "%%Q"=="Q1" set "MONTHS=01 02 03"
    if "%%Q"=="Q2" set "MONTHS=04 05 06"
    if "%%Q"=="Q3" set "MONTHS=07 08 09"
    if "%%Q"=="Q4" set "MONTHS=10 11 12"

    for %%M in (!MONTHS!) do (
        set "SRC=%BASE_DIR%sales_2024%%M.csv"
        if exist "!SRC!" (
            if "!FIRST!"=="1" (
                type "!SRC!" >> "!OUT!"
                set "FIRST=0"
            ) else (
                more +1 "!SRC!" >> "!OUT!"
            )
            echo  [追加] sales_2024%%M.csv → %%Q_combined.csv
        ) else (
            echo  [SKIP] sales_2024%%M.csv が見つかりません
        )
    )

    if "!FIRST!"=="0" (
        echo [OK] %%Q 完了 → !OUT!
    ) else (
        echo [WARN] %%Q の CSV が1件もありませんでした
    )
)

実践例2:複数システムのログCSVをサブフォルダから再帰収集して統合する

@echo off
setlocal enabledelayedexpansion

set "LOG_ROOT=C:worklogs"
set "OUT=C:workall_logs.csv"
set "FIRST=1"
set "FILE_COUNT=0"

if exist "%OUT%" del "%OUT%"

echo ===== ログCSV統合開始: %date% %time% =====

for /r "%LOG_ROOT%" %%F in (log_*.csv) do (
    if /i not "%%~fF"=="%OUT%" (
        set /a FILE_COUNT+=1
        if "!FIRST!"=="1" (
            :: ヘッダーにソース列を追加
            for /f "usebackq eol=| tokens=* delims=" %%H in ("%%F") do (
                echo %%H,source_system >> "%OUT%"
                goto :hdr_done
            )
            :hdr_done
            set "FIRST=0"
        )
        :: データ行にシステム名(上位フォルダ名)を追加
        for /f "usebackq eol=| skip=1 tokens=* delims=" %%L in ("%%F") do (
            echo %%L,%%~dpF >> "%OUT%"
        )
        echo  [追加] %%~nxF
    )
)

set "TOTAL=0"
for /f %%C in ('find /c /v "" ^< "%OUT%"') do set "TOTAL=%%C"

echo ===== 完了: !FILE_COUNT! ファイル / !TOTAL! 行 → %OUT% =====

実践例3:PowerShell で UTF-8 CSV を結合して重複除去・ソートまで行う

文字コードが混在する環境や、大量ファイルを高速処理したい場合に有効です。

@echo off
echo CSV結合処理を開始します...

powershell -NoProfile -ExecutionPolicy Bypass -Command "
    $srcDir  = "C:workcsv"
    $outFile = "C:workcombined_final.csv"
    $exclude = [System.IO.Path]::GetFileName($outFile)

    $files = Get-ChildItem -Path $srcDir -Filter "*.csv" -Recurse | Where-Object { $_.Name -ne $exclude } | Sort-Object Name

    if ($files.Count -eq 0) { Write-Error "CSV が見つかりません"; exit 1 }

    $allRows = @()
    $header  = $null

    foreach ($f in $files) {
        $rows = Import-Csv -Path $f.FullName -Encoding UTF8
        if ($header -eq $null) { $header = ($rows | Select-Object -First 1).PSObject.Properties.Name }
        $allRows += $rows
    }

    # 重複除去・ソート
    $result = $allRows | Sort-Object ($header[0]) -Unique

    $result | Export-Csv -Path $outFile -Encoding UTF8 -NoTypeInformation
    Write-Host "[OK] $($result.Count) 行(重複除去後)→ $outFile"
"

if errorlevel 1 (
    echo [ERROR] PowerShell 処理に失敗しました
    exit /b 1
)
echo [完了]

12. まとめ:CSV結合方法の使い分け表

方法 ヘッダー制御 再帰対応 文字コード 向いているケース
copy *.csv out.csv 全ファイルのヘッダーが混入 × Shift-JIS ヘッダー不要な単純結合
for ループ + type/more +1 1行保持可 for /r で可 Shift-JIS 標準的な業務CSV結合
for /r 再帰 1行保持可 Shift-JIS サブフォルダ分散CSV
PowerShell Get-Content 1行保持可 Recurse対応 UTF-8対応 UTF-8 CSV・大量ファイル
PowerShell Import-Csv 自動管理 Recurse対応 UTF-8対応 重複除去・ソート・加工が必要な場合

CSV 関連の他の記事として バッチファイルでCSVファイルを読み込む方法完全ガイドCSVファイルを分割する方法完全ガイドバッチファイルでCSVを結合する方法完全ガイド もあわせて参照してください。

FAQ

Q. ヘッダーを1行だけ保持してCSVを結合するにはどうすればいいですか?
A. フラグ変数 FIRST を使い、最初のファイルは type で全行出力し、2ファイル目以降は more +1 または for /f skip=1 でヘッダーをスキップして追記します(→ 2節)。
Q. 出力ファイル自身が結合対象に含まれてしまいます。
A. if /i not "%%F"=="combined.csv" の条件でスキップするか、出力先を別フォルダに指定してください。for /r を使う場合はフルパス変数と %%~fF で比較する必要があります(→ 落とし穴5)。
Q. UTF-8 の CSV を結合すると文字化けします。
A. バッチの for /f は Shift-JIS で処理するため UTF-8 CSV は文字化けします。PowerShell の Get-Content -Encoding UTF8 または Import-Csv -Encoding UTF8 で処理してください(→ 7節)。
Q. サブフォルダの CSV もまとめて結合したいです。
A. for /r "フォルダ" %%F in (*.csv) do でサブフォルダを再帰検索できます。ただし出力ファイルのスキップにはフルパス比較が必要です(→ 3節・落とし穴5)。
Q. 結合後に重複行を取り除きたいです。
A. ヘッダー行を除いたデータ部分を sort /unique でソート・重複除去し、ヘッダーと再結合します(→ 8節)。複雑な条件での重複除去は PowerShell の Sort-Object -Unique を推奨します。
Q. 結合時にどのファイルからのデータか記録したいです。
A. 各データ行に元ファイル名を追加するには、echo %%L,%%~nF >> out.csv のように %%~nF(ファイル名部分)をカンマ区切りで追記します。ヘッダー行には source_file などの列名を追加してください(→ 5節)。