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

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

Windowsの業務自動化でよく求められるのが、バッチファイルだけでCSVやExcelファイルを自動生成する仕組みです。毎日同じ手順でファイルを作っているなら、バッチに任せてしまえば作業時間をゼロにできます。

本記事では「純バッチのechoリダイレクト」「PowerShell併用でUTF-8 BOM付き出力」「Excel COMオートメーションで.xlsxを直接生成」という3つのアプローチを段階的に解説します。基本的なCSV作成から、日次レポートの自動生成・メール送信まで、すぐに現場で使えるテンプレートを揃えています。

バッチ初心者でも順番に読めばそのまま実装できるよう、コード全文+動作の仕組み+よくあるトラブル対策をセットで掲載しています。Excelを手動で開く作業から解放されたい方はぜひ最後まで読んでください。

この記事で学べること

  • 純バッチ・PowerShell併用・Excel COM の3アプローチの使い分け
  • CSVエスケープ規則(カンマ・ダブルクォート・改行)の正確な処理方法
  • systeminfo / wmic / dir の出力をそのままCSVに変換する実用例
  • UTF-8 BOM付きCSVをバッチから生成してExcelで文字化けを防ぐ方法
  • PowerShell + Excel COMで.xlsxを直接生成し、書式まで設定する方法
  • 日次レポート自動生成・ファイル棚卸し・wmicレポートの実践テンプレート3本
  • 文字化け・Excelプロセス残留・列ずれなどよくあるトラブルの対処法
スポンサーリンク

バッチでCSV/Excelを生成する3つのアプローチ比較

まず全体像を把握しておきましょう。バッチからCSVやExcelを生成する方法は大きく3つに分類できます。

アプローチ 出力形式 外部依存 難易度 向いている用途
方法1 純バッチ echo CSV(Shift-JIS) なし ★☆☆ シンプルなログ・コマンド結果の記録
方法2 PowerShell併用 CSV(UTF-8 BOM) PowerShell(標準搭載) ★★☆ 日本語データ・既存CSVの文字コード変換
方法3 Excel COM .xlsx(Excelブック) Excel(インストール必須) ★★★ 書式付きレポート・複数シート・自動配布

ポイント:「とにかく動けばいい」なら方法1、「Excelで開いたとき文字化けしたくない」なら方法2、「見栄えの良いレポートを自動配布したい」なら方法3 を選んでください。方法は組み合わせてもOKです。

方法1:純バッチでCSVを生成する基本(echo リダイレクト)

最もシンプルな方法は echo コマンドと >(上書き)/>>(追記)リダイレクトを使うやり方です。外部ツール不要で、どのWindows環境でも動作します。

basic-csv.bat — echoでCSVヘッダー+データ行を生成
@rem basic-csv.bat — 最もシンプルなCSV生成
@echo off
setlocal

rem 出力先ファイル名(日付付き)
set OUTFILE=output_%date:~0,4%%date:~5,2%%date:~8,2%.csv

rem ヘッダー行(> で新規作成/上書き)
echo 名前,部署,スコア > %OUTFILE%

rem データ行(>> で追記)
echo 田中太郎,営業部,92 >> %OUTFILE%
echo 鈴木花子,開発部,88 >> %OUTFILE%
echo 佐藤次郎,総務部,75 >> %OUTFILE%

echo 作成完了: %OUTFILE%
endlocal

注意:echo 文字列 > ファイル と書くと行末にスペースが入ることがあります。echo 文字列>ファイル>の直前にスペースを入れない)とするとスペースなしで出力できます。ただし可読性が下がるため、データ内容に影響が出ない場合はスペースありでも問題ありません。

日付フォーマットについて

%date% の形式はロケールによって異なります。%date:~0,4% などのスライスが正しく動くかどうかは環境依存です。確実にするには以下のように wmic で取得する方法も使えます。

date-safe.bat — ロケール非依存の日付取得
@echo off
setlocal enabledelayedexpansion

rem wmicでISO形式 YYYYMMDD を取得
for /f "tokens=2 delims==" %%D in (
  'wmic os get LocalDateTime /value'
) do set DT=%%D

set TODAY=!DT:~0,8!
set OUTFILE=report_%TODAY%.csv
echo 出力ファイル: %OUTFILE%

CSVエスケープ規則の解説

CSVは単純に見えますが、データにカンマや改行が含まれる場合は適切なエスケープが必要です。RFC 4180 に基づくルールを理解しておきましょう。

データに含まれる文字 処理ルール バッチでの書き方例
カンマ , フィールド全体をダブルクォートで囲む echo "田中,太郎",営業,92
ダブルクォート " ""(二重)に置換してフィールドを"で囲む echo "彼は""天才""だ",部署
改行(CRLF) フィールドを"で囲めばそのまま含められる(純バッチでは困難) PowerShell側で処理推奨
先頭・末尾スペース 保持したい場合は"で囲む echo " スペースあり "
特殊文字 & | < > ^ ^でエスケープするかダブルクォートで囲む echo "100^%達成"

ポイント:バッチ内でダブルクォートを含む文字列を echo で出力すると、CMD.EXE がその " を取り込もうとして予期しない動作になることがあります。ダブルクォートを含むデータは PowerShell 側で組み立てるのが安全です。

方法1:ループでデータ行を動的に追記する

固定データではなく、変数や配列的なデータを繰り返し処理してCSVに書き出すパターンを見ていきます。バッチには配列がないため、for ループや番号付き変数で代替します。

loop-csv.bat — for ループで複数行を動的生成
@echo off
setlocal enabledelayedexpansion

set OUTFILE=employees.csv
echo ID,名前,部署,スコア > %OUTFILE%

rem データを番号付き変数で定義
set DATA[1]=1,田中太郎,営業部,92
set DATA[2]=2,鈴木花子,開発部,88
set DATA[3]=3,佐藤次郎,総務部,75
set DATA[4]=4,高橋美咲,人事部,81
set DATA[5]=5,伊藤健一,経理部,79

for /l %%i in (1,1,5) do (
  echo !DATA[%%i]! >> %OUTFILE%
)

echo %OUTFILE% を作成しました。
endlocal

外部テキストファイルにデータを持たせて、それをCSVに変換するパターンも実用的です。

txt2csv.bat — テキストファイルの各行をCSVに変換
@echo off
setlocal enabledelayedexpansion

set INFILE=data.txt
set OUTFILE=data.csv
set ROW=0

echo 行番号,内容 > %OUTFILE%

for /f "usebackq delims=" %%L in ("%INFILE%") do (
  set /a ROW+=1
  echo !ROW!,%%L >> %OUTFILE%
)

echo 変換完了: %ROW%endlocal

方法1:コマンド出力(systeminfo / wmic)をCSV化する実用例

バッチから systeminfo や wmic を呼び出してその結果を整形し、CSVに保存するパターンはシステム管理・棚卸し業務で頻繁に使われます。

sysinfo-csv.bat — systeminfo の主要項目をCSVに出力
@echo off
setlocal enabledelayedexpansion

set OUTFILE=sysinfo.csv
echo 項目,値 > %OUTFILE%

rem wmic で各項目を個別取得(区切りが明確で解析しやすい)
for /f "tokens=2 delims==" %%V in (
  'wmic computersystem get Name /value'
) do if not "%%V"=="" echo コンピュータ名,%%V >> %OUTFILE%

for /f "tokens=2 delims==" %%V in (
  'wmic os get Caption /value'
) do if not "%%V"=="" echo OS,%%V >> %OUTFILE%

for /f "tokens=2 delims==" %%V in (
  'wmic os get TotalVisibleMemorySize /value'
) do if not "%%V"=="" (
  set /a MEM_MB=%%V / 1024
  echo メモリ(MB),!MEM_MB! >> %OUTFILE%
)

for /f "tokens=2 delims==" %%V in (
  'wmic cpu get Name /value'
) do if not "%%V"=="" echo CPU,%%V >> %OUTFILE%

echo 作成: %OUTFILE%
endlocal

注意:wmic は Windows 10 21H1 以降で非推奨(deprecated)となっており、将来的に削除される予定です。Windows 11 環境では Get-WmiObjectGet-CimInstance(PowerShell)の利用を推奨します。現時点では動作しますが、長期運用するスクリプトは方法2・3のPowerShellベースへ移行することを検討してください。

方法1:dirコマンドでファイル一覧をCSV化する

フォルダ内のファイル一覧をCSV化するのは、ファイル棚卸しや更新日チェックに非常に便利です。dir コマンドの出力を for /f で解析して整形します。

dir2csv.bat — dir コマンドでファイル一覧をCSV化
@echo off
setlocal enabledelayedexpansion

set TARGET_DIR=C:\work
set OUTFILE=filelist.csv

echo ファイル名,更新日,サイズ(Bytes),拡張子 > %OUTFILE%

rem /b でファイル名のみ取得し、FOR で詳細情報を追加
for /f "usebackq delims=" %%F in (
  'dir /b /a-d "%TARGET_DIR%"'
) do (
  rem ファイルの詳細情報を取得
  for %%A in ("%TARGET_DIR%\%%F") do (
    set FDATE=%%~tA
    set FSIZE=%%~zA
    set FEXT=%%~xA
  )
  echo "%%F",!FDATE!,!FSIZE!,!FEXT! >> %OUTFILE%
)

echo 完了: %OUTFILE%
endlocal

%%~tA は更新日時、%%~zA はサイズ(バイト)、%%~xA は拡張子を表すバッチの特殊変数修飾子です。これらを組み合わせるとファイルの属性を簡単に取得できます。

FOR の変数修飾子 チートシート

  • %%~fA:フルパス
  • %%~nA:拡張子なしのファイル名
  • %%~xA:拡張子(.txt など)
  • %%~zA:ファイルサイズ(バイト)
  • %%~tA:最終更新日時
  • %%~dA:ドライブ文字
  • %%~pA:パス部分(ファイル名を除く)

方法2:PowerShell併用でUTF-8 BOM付きCSVを出力する

純バッチの echo リダイレクトでは、出力される文字コードはコマンドプロンプトのコードページ(通常 Shift-JIS = CP932)になります。これをExcelで開くと問題ありませんが、他のシステムや他OS との連携では UTF-8 が求められることが多いです。

さらに Excel は UTF-8 の CSV を開くとき、BOM(Byte Order Mark)が付いていないと文字化けする仕様があります。そこでバッチから PowerShell を呼び出して UTF-8 BOM付きのCSVを生成します。

utf8bom-csv.bat — PowerShell経由でUTF-8 BOM付きCSVを出力
@echo off
setlocal

set OUTFILE=output_utf8bom.csv

rem PowerShell に渡すスクリプト(ヒアドキュメント的に変数で定義)
set PS_CMD=$rows = @(
set PS_CMD=$rows = @('名前,部署,スコア','田中太郎,営業部,92','鈴木花子,開発部,88');
set PS_CMD=%PS_CMD%$enc=[System.Text.UTF8Encoding]::new($true);
set PS_CMD=%PS_CMD%[System.IO.File]::WriteAllLines('%OUTFILE%',$rows,$enc)

powershell -NoProfile -Command "$rows=@('名前,部署,スコア','田中太郎,営業部,92','鈴木花子,開発部,88'); $enc=[System.Text.UTF8Encoding]::new($true); [System.IO.File]::WriteAllLines('%OUTFILE%',$rows,$enc)"

echo 完了: %OUTFILE%
endlocal

PowerShell の [System.Text.UTF8Encoding]::new($true) の引数 $true が BOM付きを指定するパラメーターです。$false にすると BOM なしになります。

より実用的なパターンとして、PowerShell スクリプトを別ファイルに分けてバッチから呼び出す方法を紹介します。

generate-csv.ps1 — PowerShell側のCSV生成スクリプト
# generate-csv.ps1
param(
    [string]$OutputPath = "output.csv"
)

# データをPSオブジェクト配列で定義
$data = @(
    [PSCustomObject]@{ 名前="田中太郎"; 部署="営業部"; スコア=92 }
    [PSCustomObject]@{ 名前="鈴木花子"; 部署="開発部"; スコア=88 }
    [PSCustomObject]@{ 名前="佐藤次郎"; 部署="総務部"; スコア=75 }
)

# Export-Csv で UTF-8 BOM付き出力
$data | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8

Write-Host "CSV出力完了: $OutputPath"
run-ps-csv.bat — PowerShellスクリプトを呼び出すバッチ
@echo off
setlocal

set PS_SCRIPT=%~dp0generate-csv.ps1
set OUT=%~dp0output.csv

powershell -NoProfile -ExecutionPolicy Bypass ^
  -File "%PS_SCRIPT%" -OutputPath "%OUT%"

if %errorlevel% neq 0 (
  echo エラーが発生しました。
  exit /b 1
)
echo 完了: %OUT%
endlocal

方法2:既存CSVのエンコードをUTF-8に変換する

既に Shift-JIS(CP932)で作成されたCSVを UTF-8 BOM付きに変換するだけのバッチも実用的です。レガシーシステムから出力されたCSVを加工して他システムに連携するケースで活躍します。

convert-encoding.bat — Shift-JIS → UTF-8 BOM付きに変換
@echo off
setlocal

set INFILE=input_sjis.csv
set OUTFILE=output_utf8bom.csv

powershell -NoProfile -Command ^
  "$content = [System.IO.File]::ReadAllText('%INFILE%', [System.Text.Encoding]::GetEncoding(932)); ^
   $enc = [System.Text.UTF8Encoding]::new($true); ^
   [System.IO.File]::WriteAllText('%OUTFILE%', $content, $enc)"

if %errorlevel%==0 (
  echo 変換成功: %OUTFILE%
) else (
  echo 変換失敗
)
endlocal

ポイント:GetEncoding(932) が Shift-JIS(CP932)を指定するコードです。EUC-JP なら GetEncoding(51932)、UTF-16 LE(BOM付き)なら GetEncoding(1200) を使います。変換元エンコードが不明な場合は PowerShell の Get-Content にオートディテクト機能がないため、事前に確認が必要です。

方法3:PowerShell + Excel COMで.xlsxを直接生成する

CSVではなく書式付きのExcelブック(.xlsx)を直接生成したい場合は、PowerShell から Excel の COM オブジェクトを操作します。この方法は Excel がインストールされている環境でのみ動作しますが、セルの書式・フォント・背景色まで自由に制御できます。

create-xlsx.ps1 — Excel COMで.xlsxを直接生成する基本形
# create-xlsx.ps1 — Excel COMオートメーション基本形
param([string]$OutputPath = "report.xlsx")

# Excel を非表示で起動
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false
$excel.DisplayAlerts = $false

try {
    $wb = $excel.Workbooks.Add()
    $ws = $wb.Worksheets.Item(1)
    $ws.Name = "レポート"

    # ヘッダー行を書き込む
    $headers = @("ID", "名前", "部署", "スコア", "評価")
    for ($c = 1; $c -le $headers.Count; $c++) {
        $ws.Cells(1, $c).Value2 = $headers[$c - 1]
    }

    # データ行を書き込む
    $data = @(
        @(1, "田中太郎", "営業部", 92, "S"),
        @(2, "鈴木花子", "開発部", 88, "A"),
        @(3, "佐藤次郎", "総務部", 75, "B")
    )

    $row = 2
    foreach ($d in $data) {
        for ($c = 1; $c -le $d.Count; $c++) {
            $ws.Cells($row, $c).Value2 = $d[$c - 1]
        }
        $row++
    }

    # .xlsx 形式で保存(51 = xlOpenXMLWorkbook)
    $absPath = [System.IO.Path]::GetFullPath($OutputPath)
    $wb.SaveAs($absPath, 51)
    Write-Host "保存完了: $absPath"
}
finally {
    $wb.Close($false)
    $excel.Quit()
    [System.Runtime.InteropServices.Marshal]::ReleaseComObject($excel) | Out-Null
    [System.GC]::Collect()
}

注意:finally ブロックで必ず $excel.Quit()ReleaseComObject を呼び出してください。これを省略するとExcelのプロセスがバックグラウンドに残り続け、次回実行時にエラーになったりリソースを圧迫したりします。

方法3:ヘッダー太字・列幅自動調整・シート名設定

Excel COMでは書式設定も細かくコントロールできます。業務レポートとして見栄えを整える主要な設定を追加したバージョンです。

create-xlsx-styled.ps1 — ヘッダー書式・列幅自動調整付き
# (ブック・シート作成・データ書き込みは前述と同じ)
# データ書き込み後に以下を追加

# ヘッダー行の書式設定
$headerRange = $ws.Range($ws.Cells(1,1), $ws.Cells(1, $headers.Count))
$headerRange.Font.Bold = $true
$headerRange.Font.Color = -16777216        # 黒
$headerRange.Interior.Color = 0x4472C4      # 青(ExcelのデフォルトTable色)
$headerRange.Font.Color = -1               # 白文字

# 交互行に背景色を設定
$lastRow = $row - 1
for ($r = 2; $r -le $lastRow; $r++) {
    if ($r % 2 -eq 0) {
        $ws.Range(
            $ws.Cells($r, 1),
            $ws.Cells($r, $headers.Count)
        ).Interior.Color = 0xDCE6F1         # 薄い青
    }
}

# 全列の幅を自動調整
$ws.Columns.AutoFit() | Out-Null

# 枠線を設定
$dataRange = $ws.Range(
    $ws.Cells(1, 1),
    $ws.Cells($lastRow, $headers.Count)
)
$dataRange.Borders.LineStyle = 1           # xlContinuous
$dataRange.Borders.Weight = 2             # xlThin

# 先頭行を固定表示
$ws.Rows(2).Select() | Out-Null
$ws.Application.ActiveWindow.FreezePanes = $true

Excel COMでよく使う設定値

  • SaveAs 形式番号: 51 = .xlsx、52 = .xlsm(マクロ有効)、56 = .xls(旧形式)
  • 色指定: Interior.Color に RGB値(0xRRGGBB)または Excel の XlRgbColor 定数を渡す
  • -1(= 0xFFFFFF ではなく xlAutomatic)で自動色になる点に注意
  • 列幅: Columns("A:E").ColumnWidth = 20 で数値指定、AutoFit() で自動
  • 行高さ: Rows(1).RowHeight = 25

方法3:CSV→.xlsx変換バッチ

既存のCSVを読み込んでExcelブックに変換する実用的なパターンです。毎日出力されるCSVを自動的に.xlsxに変換してメール添付する、といったワークフローに組み込めます。

csv2xlsx.bat — CSVを.xlsxに変換するバッチ(PowerShell呼び出し)
@echo off
setlocal

set CSVFILE=%~1
if "%CSVFILE%"=="" (
  echo 使い方: csv2xlsx.bat [CSVファイルパス]
  exit /b 1
)

set XLSXFILE=%~dpn1.xlsx

powershell -NoProfile -ExecutionPolicy Bypass -Command ^
"$csv=[System.IO.Path]::GetFullPath('%CSVFILE%');" ^
"$xlsx=[System.IO.Path]::GetFullPath('%XLSXFILE%');" ^
"$xl=New-Object -ComObject Excel.Application;" ^
"$xl.Visible=$false;$xl.DisplayAlerts=$false;" ^
"try{" ^
"  $wb=$xl.Workbooks.Open($csv);" ^
"  $wb.SaveAs($xlsx,51);" ^
"  Write-Host '変換完了:'$xlsx" ^
"}finally{" ^
"  $wb.Close($false);" ^
"  $xl.Quit();" ^
"  [System.Runtime.InteropServices.Marshal]::ReleaseComObject($xl)|Out-Null;" ^
"  [System.GC]::Collect()" ^
"}"

endlocal

引数でCSVファイルを渡す方式にしておくと、タスクスケジューラや他のバッチから汎用的に呼び出せます。

ポイント:Workbooks.Open でCSVを開くと Excel が自動的に区切り文字を認識して取り込みます。ただし列の型推定が走るため、先頭ゼロのコード値(001 など)が数値として変換されることがあります。これを防ぐには PowerShell で CSVを行ごとに読み込んでセルに文字列として書き込む方が確実です。

実践テンプレート1:日次レポートをCSVで自動生成してメール送信

毎朝決まったデータをCSVにまとめてメールで配信する、という業務自動化の典型パターンです。タスクスケジューラと組み合わせれば完全無人化できます。

daily-report.bat — 日次レポートCSV生成+メール送信
@echo off
setlocal enabledelayedexpansion

rem ===== 設定 =====
set REPORT_DIR=C:\reports
set SMTP_SERVER=smtp.example.com
set MAIL_TO=manager@example.com
set MAIL_FROM=report@example.com

rem 今日の日付を取得
for /f "tokens=2 delims==" %%D in (
  'wmic os get LocalDateTime /value'
) do set DT=%%D
set TODAY=!DT:~0,8!
set TODAY_DISP=!DT:~0,4!/!DT:~4,2!/!DT:~6,2!

set OUTFILE=%REPORT_DIR%\daily_%TODAY%.csv

rem レポートディレクトリ作成
if not exist %REPORT_DIR% mkdir %REPORT_DIR%

rem ===== CSV生成 =====
echo 日付,サーバー,CPU使用率,メモリ使用率,ディスク空き(GB) > %OUTFILE%

rem CPUとメモリをwmicから取得
for /f "tokens=2 delims==" %%C in (
  'wmic cpu get LoadPercentage /value'
) do if not "%%C"=="" set CPU=%%C

for /f "tokens=2 delims==" %%F in (
  'wmic os get FreePhysicalMemory /value'
) do if not "%%F"=="" set MEM_FREE=%%F

for /f "tokens=2 delims==" %%T in (
  'wmic os get TotalVisibleMemorySize /value'
) do if not "%%T"=="" set MEM_TOTAL=%%T

set /a MEM_USED_PCT=(%MEM_TOTAL% - %MEM_FREE%) * 100 / %MEM_TOTAL%

rem Cドライブの空き容量をGB単位で
for /f "tokens=2 delims==" %%G in (
  'wmic logicaldisk where DeviceID="C:" get FreeSpace /value'
) do if not "%%G"=="" set DISK_FREE=%%G
set /a DISK_GB=%DISK_FREE% / 1073741824

rem CSV書き込み
echo %TODAY_DISP%,%COMPUTERNAME%,%CPU%%,%MEM_USED_PCT%%,%DISK_GB% >> %OUTFILE%

rem ===== メール送信(PowerShell経由)=====
powershell -NoProfile -Command ^
"Send-MailMessage -SmtpServer '%SMTP_SERVER%' -From '%MAIL_FROM%' -To '%MAIL_TO%' ^
 -Subject '日次レポート %TODAY_DISP%' -Body '添付のCSVを確認してください。' ^
 -Attachments '%OUTFILE%' -Encoding UTF8"

echo 完了: %OUTFILE%
endlocal

タスクスケジューラへの登録手順

  • スタートメニューで「タスクスケジューラ」を開く
  • 「タスクの作成」→ トリガーで「毎日 8:00」などを設定
  • 操作タブで「プログラムの開始」→ cmd.exe、引数に /c "C:\scripts\daily-report.bat"
  • 「最上位の特権で実行」にチェックを入れると wmic が確実に動作する
  • 「ユーザーがログオンしているかどうかにかかわらず実行する」でバックグラウンド実行が可能

実践テンプレート2:複数フォルダのファイル棚卸しCSV

複数のフォルダを横断して全ファイルの情報をCSV一覧にまとめる棚卸しスクリプトです。ネットワークドライブも対象にできます。

inventory.bat — 複数フォルダのファイル棚卸しCSV
@echo off
setlocal enabledelayedexpansion

set OUTFILE=inventory.csv
set COUNT=0

rem ヘッダー
echo フォルダ,ファイル名,拡張子,サイズ(KB),更新日時,フルパス > %OUTFILE%

rem 対象フォルダのリストを設定(スペース区切りで列挙)
for %%T in (
  "C:\work"
  "C:\projects"
  "D:\archive"
) do (
  if exist %%T (
    rem /s でサブフォルダも再帰、/b でファイル名のみ
    for /r %%T %%F in (*.*) do (
      set FDIR=%%~dpF
      set FNAME=%%~nxF
      set FEXT=%%~xF
      set /a FSIZE_KB=%%~zF / 1024
      set FDATE=%%~tF
      set FPATH=%%~fF
      echo "!FDIR!","!FNAME!","!FEXT!",!FSIZE_KB!,"!FDATE!","!FPATH!" >> %OUTFILE%
      set /a COUNT+=1
    )
  ) else (
    echo [スキップ] %%T が存在しません
  )
)

echo 棚卸し完了: %COUNT% ファイル → %OUTFILE%
endlocal

注意:ファイル名やパスにカンマが含まれる場合、CSV の列がずれます。上記の例ではフィールドをダブルクォートで囲んでいますが、ファイル名にダブルクォートが含まれる場合(通常はありませんが)はさらに処理が必要です。特殊文字が多い環境では PowerShell の Export-Csv を使う方が確実です。

実践テンプレート3:wmic情報をExcelレポートに変換

複数台のPCのシステム情報を収集してExcelレポートにまとめる、IT管理部門で定番の自動化パターンです。ローカル実行版と、PSRemoting(リモート実行)版の両方を示します。

pc-inventory.bat — PC棚卸し情報をExcelレポートに変換
@echo off
setlocal

set OUTFILE=pc_inventory.xlsx
set PS_SCRIPT=%~dp0pc-inventory.ps1

powershell -NoProfile -ExecutionPolicy Bypass ^
  -File "%PS_SCRIPT%" -OutputPath "%~dp0%OUTFILE%"

if %errorlevel%==0 (
  echo Excelレポート作成完了: %OUTFILE%
) else (
  echo エラーが発生しました
)
endlocal
pc-inventory.ps1 — wmic情報を収集してExcelに書き込む
# pc-inventory.ps1
param([string]$OutputPath = "pc_inventory.xlsx")

# システム情報を取得(Get-CimInstance を使用、wmicより安定)
$cs  = Get-CimInstance Win32_ComputerSystem
$os  = Get-CimInstance Win32_OperatingSystem
$cpu = Get-CimInstance Win32_Processor | Select-Object -First 1
$mem = [math]::Round($cs.TotalPhysicalMemory / 1GB, 1)
$disks = Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3"

$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false
$excel.DisplayAlerts = $false

try {
    $wb = $excel.Workbooks.Add()
    $ws = $wb.Worksheets.Item(1)
    $ws.Name = "PC棚卸し"

    # ヘッダー
    $headers = @("項目", "値")
    for ($c=1; $c -le $headers.Count; $c++) {
        $ws.Cells(1,$c).Value2 = $headers[$c-1]
        $ws.Cells(1,$c).Font.Bold = $true
        $ws.Cells(1,$c).Interior.Color = 0x4472C4
        $ws.Cells(1,$c).Font.Color = 0xFFFFFF
    }

    # データ
    $rows = @(
        @("コンピュータ名", $env:COMPUTERNAME),
        @("OS", $os.Caption),
        @("OSビルド", $os.BuildNumber),
        @("CPU", $cpu.Name),
        @("CPUコア数", $cpu.NumberOfCores),
        @("メモリ(GB)", $mem),
        @("ユーザー名", $env:USERNAME),
        @("収集日時", (Get-Date -Format "yyyy/MM/dd HH:mm:ss"))
    )
    # ディスク情報を追加
    foreach ($d in $disks) {
        $free = [math]::Round($d.FreeSpace / 1GB, 1)
        $size = [math]::Round($d.Size / 1GB, 1)
        $rows += ,@("ドライブ $($d.DeviceID)", "空き ${free}GB / 全体 ${size}GB")
    }

    $r = 2
    foreach ($row in $rows) {
        $ws.Cells($r, 1).Value2 = $row[0]
        $ws.Cells($r, 2).Value2 = $row[1]
        $r++
    }

    $ws.Columns.AutoFit() | Out-Null
    $absPath = [System.IO.Path]::GetFullPath($OutputPath)
    $wb.SaveAs($absPath, 51)
    Write-Host "保存: $absPath"
}
finally {
    $wb.Close($false)
    $excel.Quit()
    [System.Runtime.InteropServices.Marshal]::ReleaseComObject($excel) | Out-Null
    [System.GC]::Collect()
}

応用:複数シートを持つExcelブックを生成する

実務では「月別に別シートへ書き込む」「部署ごとにシートを分ける」といったケースが頻繁にあります。Excel COM では複数シートを柔軟に操作できます。

multi-sheet.ps1 — 部署ごとに別シートへデータを書き込む
# multi-sheet.ps1
param([string]$OutputPath = "multi_report.xlsx")

$departments = @{
    "営業部" = @(
        @("田中太郎", 92), @("山田花子", 85)
    )
    "開発部" = @(
        @("鈴木一郎", 78), @("佐藤美咲", 95)
    )
    "総務部" = @(
        @("高橋健一", 70)
    )
}

$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false
$excel.DisplayAlerts = $false

try {
    $wb = $excel.Workbooks.Add()

    # 既存のシートを削除して後で追加し直す
    while ($wb.Worksheets.Count -gt 1) {
        $wb.Worksheets.Item($wb.Worksheets.Count).Delete()
    }

    $firstSheet = $true
    foreach ($dept in $departments.Keys) {
        if ($firstSheet) {
            $ws = $wb.Worksheets.Item(1)
            $firstSheet = $false
        } else {
            $ws = $wb.Worksheets.Add([System.Reflection.Missing]::Value, $wb.Worksheets.Item($wb.Worksheets.Count))
        }
        $ws.Name = $dept

        # ヘッダー
        $ws.Cells(1,1).Value2 = "名前"
        $ws.Cells(1,2).Value2 = "スコア"
        $ws.Range($ws.Cells(1,1), $ws.Cells(1,2)).Font.Bold = $true

        # データ行
        $r = 2
        foreach ($row in $departments[$dept]) {
            $ws.Cells($r,1).Value2 = $row[0]
            $ws.Cells($r,2).Value2 = $row[1]
            $r++
        }
        $ws.Columns.AutoFit() | Out-Null
    }

    $absPath = [System.IO.Path]::GetFullPath($OutputPath)
    $wb.SaveAs($absPath, 51)
    Write-Host "保存: $absPath"
}
finally {
    $wb.Close($false)
    $excel.Quit()
    [System.Runtime.InteropServices.Marshal]::ReleaseComObject($excel) | Out-Null
    [System.GC]::Collect()
}

応用:バッチでCSVに自動フィルター・集計行を追加する

Excel COMを使うと、生成したシートにオートフィルターSUM関数による集計行を自動で設定できます。人手でやっていた「フィルターを設定してから合計を確認する」という作業を完全に排除できます。

autofilter-sum.ps1 — オートフィルター+集計行の設定
# データ書き込み後に以下を追加

# オートフィルターをヘッダー行に設定
$ws.Rows(1).AutoFilter() | Out-Null

# 集計行(データ最終行の1行下)
$sumRow = $lastRow + 1
$ws.Cells($sumRow, 1).Value2 = "合計"
$ws.Cells($sumRow, 1).Font.Bold = $true

# スコア列(D列)にSUM関数を設定
$scoreColLetter = "D"
$ws.Cells($sumRow, 4).Formula = "=SUM(${scoreColLetter}2:${scoreColLetter}${lastRow})"
$ws.Cells($sumRow, 4).Font.Bold = $true

# 集計行に背景色
$ws.Range(
    $ws.Cells($sumRow, 1),
    $ws.Cells($sumRow, $headers.Count)
).Interior.Color = 0xFFF2CC   # 薄い黄

# 印刷設定(A4横向き)
$ws.PageSetup.Orientation = 2        # xlLandscape
$ws.PageSetup.PaperSize = 9          # xlPaperA4
$ws.PageSetup.FitToPagesWide = 1
$ws.PageSetup.FitToPagesTall = $false

ポイント:印刷設定まで自動化しておくと、受け取った担当者が「印刷して提出する」だけの状態でファイルを渡せます。FitToPagesWide = 1FitToPagesTall = $false の組み合わせで「横1ページに収める・縦は自動」になります。

応用:バッチ実行ログをCSVに記録する

バッチの実行結果をCSVに蓄積しておくと、いつ・何が・どのように実行されたかを後から確認できます。エラー発生時の原因調査や、定期実行の確認に役立ちます。

log-to-csv.bat — バッチの実行ログをCSVに追記する
@echo off
setlocal enabledelayedexpansion

set LOGFILE=C:\logs\batch_log.csv
set SCRIPT_NAME=%~nx0

rem ログファイルが存在しない場合はヘッダーを作成
if not exist %LOGFILE% (
  if not exist "C:\logs" mkdir "C:\logs"
  echo 実行日時,スクリプト名,ステータス,メッセージ > %LOGFILE%
)

rem 現在日時を取得
for /f "tokens=2 delims==" %%D in (
  'wmic os get LocalDateTime /value'
) do set DT=%%D
set DATETIME=!DT:~0,4!/!DT:~4,2!/!DT:~6,2! !DT:~8,2!:!DT:~10,2!:!DT:~12,2!

rem ===== ここに実際の処理を記述 =====
echo 処理を実行中...

rem 処理の成否を判定して記録
if %errorlevel%==0 (
  set STATUS=SUCCESS
  set MESSAGE=正常終了
) else (
  set STATUS=ERROR
  set MESSAGE=エラーコード %errorlevel%
)

echo "%DATETIME%","%SCRIPT_NAME%","!STATUS!","!MESSAGE!" >> %LOGFILE%
endlocal

このパターンを各バッチの先頭と末尾に組み込んでおくと、すべての定期実行バッチの実行履歴を一つのCSVで管理できます。batch_log.csv を定期的にExcelで開いてフィルタリングすれば、エラーが発生したバッチをすぐに特定できます。

実務で役立つCSVログ設計のポイント

  • 日付別ファイル分割:1ファイルに全履歴を書くと肥大化するので、月ごとにファイルを分ける(log_202601.csv など)
  • ローテーション:古いログを自動削除する処理を週次バッチに入れる(forfiles /p C:\logs /m *.csv /d -90 /c "cmd /c del @path"
  • エラー時のみ通知:STATUS が ERROR の場合だけ Send-MailMessage でアラートメールを送る
  • UTF-8 BOM付きで保存:Excelで直接開いて確認できるようにしておくと運用がラク

トラブルシューティング

バッチからCSV/Excelを生成する際によく発生する問題と対処法をまとめました。

症状 原因 対処法
Excelで開いたら日本語が文字化け 文字コードが UTF-8(BOMなし)になっている 方法2のUTF-8 BOM付き出力に切り替える。または chcp 932 でコードページをShift-JISに統一する
タスクマネージャーにEXCEL.EXEが残る $excel.Quit() が呼ばれていない(エラーで中断など) try/finally ブロックで必ずQuit+ReleaseComObjectを実行。taskkill /f /im EXCEL.EXEで手動削除
CSVを開くと列がずれる データ内にカンマや改行が含まれていてエスケープされていない フィールドをダブルクォートで囲む。または PowerShell の Export-Csv を使う(自動エスケープされる)
echo で行末に余分なスペースが入る echo 文字列 > ファイル> 前にスペースがある echo 文字列>ファイル とスペースなしにするか、PowerShellで書き込む
SaveAs で「ファイルが見つかりません」エラー 相対パスをExcelが解決できない [System.IO.Path]::GetFullPath() で必ず絶対パスに変換してからSaveAsに渡す
先頭ゼロのコード値が数値に変換される ExcelがCSVを開く際に型推定して数値化する COMでセルに書き込む際 .NumberFormat = "@"(文字列)を先に設定する。またはフィールドを ="001" 形式で書く
PowerShell の実行ポリシーエラー ExecutionPolicy が Restricted になっている バッチから呼ぶ場合は -ExecutionPolicy Bypass を付ける。または管理者権限で Set-ExecutionPolicy RemoteSigned を実行
wmic が動かない / 非推奨警告が出る Windows 11 以降でwmicは非推奨 Get-CimInstance(PowerShell)に書き換える。バッチ → PowerShell 呼び出しに移行する
for /f でデータが1行しか取れない tokens の設定誤りや、コマンドの出力に空行が含まれる "skip=1 tokens=* delims=" などを調整。空行を if not "%%L"=="" で除外する

まとめ

バッチファイルからCSV・Excelを自動生成する方法を3つのアプローチで解説しました。最後に使い分けのポイントを整理しておきます。

やりたいこと 推奨アプローチ
シンプルなログをCSVで残したい 方法1(純バッチ echo)
日本語データをExcelで文字化けなく開きたい 方法2(PowerShell + UTF-8 BOM)
既存CSVの文字コードを変換したい 方法2(エンコード変換)
書式付き.xlsxレポートを自動生成したい 方法3(Excel COM)
タスクスケジューラで毎日自動実行したい 実践テンプレート1(日次レポート)
複数フォルダのファイルを棚卸ししたい 実践テンプレート2(ファイル棚卸し)
PCのシステム情報をExcel一覧にしたい 実践テンプレート3(wmic/CIMレポート)

次のステップ

  • 今回の実践テンプレートをタスクスケジューラに登録して完全自動化する
  • wmic を使っている箇所を Get-CimInstance(PowerShell)に置き換えて将来に備える
  • Excel COMの書式設定をさらに深掘りして、グラフ付きレポートを自動生成する
  • 複数PCの情報をネットワーク越しに収集する(PSRemoting)