【bat】バッチファイルでExcelやCSVを自動生成する方法

bat

バッチファイルだけでExcelファイル(.xlsx)を直接生成するのは難しいものの、CSVを作成してExcelで開かせる手法なら標準機能で実現できます。さらにPowerShellを併用すれば、UTF-8の文字化け対策や.xlsxの自動生成まで自動化できます。ここでは「純バッチでのCSV生成」「PowerShell併用でのエンコード対策」「Excel COMを使った.xlsx出力」の三段構えで、現場でそのまま使えるサンプルを紹介します。

純バッチでCSVを生成する基本

最もシンプルなのはechoの出力をリダイレクトしてCSVを作る方法です。カンマを区切りにして1行ずつ追記していきます。改行やクォートの扱いに注意すれば、軽い帳票なら十分に実用的です。

@echo off
setlocal EnableDelayedExpansion

rem 保存先を用意(なければ作成)
set "OUTDIR=%~dp0out"
if not exist "%OUTDIR%" mkdir "%OUTDIR%"

rem 日付時刻入りのファイル名(ゼロ埋め)
for /f "tokens=1-4 delims=/ " %%a in ("%date%") do set "YMD=%%a-%%b-%%c"
set "HMS=%time: =0%"
set "HMS=%HMS::=%"
set "CSV=%OUTDIR%\report_%YMD%_%HMS:~0,6%.csv"

rem 見出し行
> "%CSV%" echo ID,Name,Price,Note

rem データ行(ダブルクォートやカンマを含む場合はCSVルールに沿って加工)
set "ID=1001"
set "NAME=りんご"
set "PRICE=120"
set "NOTE=青森産, 旬の味"
call :AppendCsv "%CSV%" "!ID!" "!NAME!" "!PRICE!" "!NOTE!"

set "ID=1002"
set "NAME=ぶどう"
set "PRICE=450"
set "NOTE=二重""引用符""の例"
call :AppendCsv "%CSV%" "!ID!" "!NAME!" "!PRICE!" "!NOTE!"

echo 出力: "%CSV%"
exit /b

:AppendCsv
rem 引数4つ(必要に応じて拡張可)
setlocal EnableDelayedExpansion
set "F=%~1"
set "A=%~2"
set "B=%~3"
set "C=%~4"
set "D=%~5"

rem CSVのエスケープ規則:カンマ・改行・ダブルクォートを含むフィールドは二重引用符で囲み、内部の " は "" に置換
for %%X in (A B C D) do (
  set "v=!%%X!"
  set "needQuote="
  echo(!v!| findstr /r /c:",\|\^\"" /c:"[!]" >nul && set "needQuote=1"
  set "v=!v:"=""!"
  if defined needQuote set "v="!v!""
  set "%%X=!v!"
)

>> "%F%" echo !A!,!B!,!C!,!D!
endlocal & exit /b

このサンプルではCSVの基本エスケープを実装しています。ダブルクォートを二重にし、カンマやダブルクォートを含むフィールドは全体をダブルクォートで囲みます。改行を含むフィールドはCSVとしては許容されますが、バッチで扱うのは難度が高いので避けるのが安全です。

コマンドの結果をCSV化する実用例

dirやwmicなどの出力を加工してCSVに落とすと、資産台帳づくりや棚卸しに役立ちます。次は特定フォルダのファイル一覧を「名前・サイズ(バイト)・最終更新日」でCSV化する例です。

@echo off
setlocal
set "TARGET=%USERPROFILE%\Documents"
set "CSV=%~dp0files.csv"
> "%CSV%" echo Name,Bytes,LastWriteTime

for /f "skip=5 tokens=1,3,*" %%A in ('dir /-c /ta "%TARGET%" ^| findstr /r "^[0-9/]"') do (
  rem 例: 2025/09/25  14:33           123,456 filename.txt
  set "DATE=%%A"
  set "TIME=%%B"
  set "REST=%%C"
  call :ParseLine "%CSV%" "%%A" "%%B" "%%C"
)
echo 出力: "%CSV%"
exit /b

:ParseLine
setlocal EnableDelayedExpansion
set "CSV=%~1"
set "DATE=%~2"
set "TIME=%~3"
set "REST=%~4"

for /f "tokens=1,*" %%S in ("!REST!") do (
  set "SIZE=%%S"
  set "NAME=%%T"
)

rem <DIR>行はスキップ
echo !SIZE! | find "<DIR>" >nul && exit /b

rem カンマ除去したサイズ
set "SIZE=!SIZE:,=!"
rem CSV出力
>> "!CSV!" echo "!NAME!",!SIZE!,"!DATE! !TIME!"
endlocal & exit /b

dirの書式はロケールやスイッチで変わるため、実運用ではfindstr条件やトークン位置を環境に合わせて調整してください。

文字化けを避けるためのUTF-8出力(PowerShell併用)

日本語環境のcmdは既定でCP932(Shift_JIS系)なので、Excelでの開き方によっては文字化けの要因になります。Excel 2016以降はBOM付きUTF-8のCSVに対応しているため、PowerShellを併用してUTF-8 BOMで書き出すのが確実です。

@echo off
setlocal

set "CSV=%~dp0utf8_report.csv"

rem CSVの内容をPowerShellのHere-StringでUTF-8(BOM)保存
powershell -NoProfile -Command ^
  "$csv = @(" ^
  "'ID,Name,Note'," ^
  "'1001,りんご,青森産'," ^
  "'1002,ぶどう,""二重引用符""の例"'" ^
  ");" ^
  "[IO.File]::WriteAllLines('%CSV%',$csv,[Text.UTF8Encoding]::new($true))"

echo 出力: "%CSV%"
endlocal

既存のCP932で作ったCSVをUTF-8に再保存したい場合も、PowerShellのGet-ContentとSet-ContentまたはWriteAllLinesを使って変換できます。

Excelを自動操作して.xlsxを生成する(Excelインストール必須)

クライアントにMicrosoft Excelがインストールされているなら、COMオートメーションで.xlsxを直接作れます。バッチからPowerShellを呼び出し、Excel.Applicationを操作して保存します。

@echo off
setlocal
set "XLSX=%~dp0report.xlsx"

powershell -NoProfile -Command ^
  "$xl = New-Object -ComObject Excel.Application;" ^
  "$xl.Visible = $false;" ^
  "$wb = $xl.Workbooks.Add();" ^
  "$ws = $wb.Worksheets.Item(1);" ^
  "$ws.Name = 'Report';" ^
  "$data = @(@('ID','Name','Price'), @('1001','りんご',120), @('1002','ぶどう',450));" ^
  "for ($r=0; $r -lt $data.Count; $r++) { for ($c=0; $c -lt $data[$r].Count; $c++) { $ws.Cells.Item($r+1,$c+1).Value2 = $data[$r][$c] } }" ^
  "$ws.Range('A1:C1').Font.Bold = $true;" ^
  "$ws.Columns.AutoFit() | Out-Null;" ^
  "$wb.SaveAs('%XLSX%',[ref]51);" ^
  "$wb.Close($true);" ^
  "$xl.Quit();" ^
  "[System.Runtime.Interopservices.Marshal]::ReleaseComObject($ws)|Out-Null;" ^
  "[System.Runtime.Interopservices.Marshal]::ReleaseComObject($wb)|Out-Null;" ^
  "[System.Runtime.Interopservices.Marshal]::ReleaseComObject($xl)|Out-Null;"

if exist "%XLSX%" (echo 作成完了: "%XLSX%") else (echo 失敗しました)
endlocal

SaveAsの51はxlOpenXMLWorkbook(.xlsx)の定数値です。サーバーなどExcelがない環境では使えないため、その場合はCSVでの運用や別ツール(例:PowerShellのImportExcelモジュール)を検討します。

CSVから.xlsxへ変換するバッチ(Excel経由)

既にCSVを作っているなら、Excelに読み込ませて.xlsxで保存し直す手もあります。

@echo off
setlocal
set "CSV=%~dp0source.csv"
set "XLSX=%~dp0converted.xlsx"

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

powershell -NoProfile -Command ^
  "$xl = New-Object -ComObject Excel.Application; $xl.Visible=$false;" ^
  "$wb = $xl.Workbooks.Open('%CSV%');" ^
  "$wb.SaveAs('%XLSX%',[ref]51);" ^
  "$wb.Close($true); $xl.Quit();" ^
  "[Runtime.InteropServices.Marshal]::ReleaseComObject($wb)|Out-Null;" ^
  "[Runtime.InteropServices.Marshal]::ReleaseComObject($xl)|Out-Null;"

echo 変換結果: "%XLSX%"
endlocal

Excelの既定区切りやエンコード解釈はOSの地域設定やCSVのBOM有無に影響されます。事前にUTF-8 BOMのCSVにしておくと誤判定が減ります。

安全に運用するためのポイント

実運用ではヘッダ固定と列順の変更禁止、エンコードの統一、ファイル名の衝突回避が重要です。日付・時刻入りの出力名とし、履歴を蓄積できるようにフォルダを分けると管理が容易になります。Excel COMを使う場合は並列実行での競合、バックグラウンドでExcelプロセスが残留しないようReleaseComObjectとQuitの確実な呼び出しを徹底します。文字化けが疑われる場合はBOM付きUTF-8での保存を第一選択にし、どうしてもレガシー環境に合わせる必要があるときのみCP932での出力を選択します。

まとめ

軽量なレポートは純バッチでCSVを生成し、文字化け対策や.xlsxが必要なときはPowerShellを併用する方針が現実的です。まずはCSVでレイアウトと列定義を固め、必要に応じてExcel COMで.xlsx化するレイヤード設計にすると、環境差に強く保守しやすい自動化が実現できます。