バッチファイルで処理を自動化するとき、「いつ・何が・どんな結果で実行されたか」を記録しておくことは非常に重要です。ログがあれば問題発生時の原因究明が格段に速くなり、定期実行スクリプトの動作確認にも役立ちます。この記事では バッチファイルのログ出力を体系的に解説します。基本のリダイレクト(> / >>)から、タイムスタンプ付きメッセージ・PC名/ユーザー名の自動記録・カッコブロックによるまとめ出力・古いログの自動削除まで、実践例3本とともにわかりやすく説明します。
>(上書き)と>>(追記)の違いと使い分け- エラー出力も同時に記録する
2>&1の使い方 - 日付・時刻を自動付与するタイムスタンプ付きログメッセージの書き方
- PC名・ユーザー名・IPアドレスなど実行環境情報をログに含める方法
- カッコブロックで複数コマンドをまとめてログに出力するテクニック
- 実践例3本(処理結果ログ・エラー通知ログ・日次バックアップログ)
1. ログ出力の基本:>(上書き)と >>(追記)
バッチファイルでログを出力する最も基本的な方法は、標準出力をファイルにリダイレクトすることです。リダイレクト(> >> 2>&1)の完全ガイドも参照してください。
| 記号 | 動作 | 主な用途 |
|---|---|---|
> |
ファイルに上書き(既存内容は消える) | 毎回新規ログファイルを作りたいとき |
>> |
ファイルに追記(既存内容は残る) | 1つのファイルにログを積み重ねるとき |
2>&1 |
標準エラー出力(2)を標準出力(1)に統合 | エラーメッセージもログに残したいとき |
>nul |
出力を破棄(画面にも出ない) | 不要なメッセージを消したいとき |
@echo off :: ─── 上書き(毎回新しいログ) ────────────────────── echo 処理開始 > log.txt echo 処理A実行 >> log.txt echo 処理完了 >> log.txt :: ─── 追記(既存ログに積み重ね) ──────────────────── echo 処理開始 >> log.txt echo 処理A実行 >> log.txt echo 処理完了 >> log.txt :: ─── エラーも含めてログに記録 ────────────────────── :: 2>&1 を付けると標準エラーもファイルに書き込まれる dir C:\存在しないフォルダ >> log.txt 2>&1 ping 192.168.1.1 >> log.txt 2>&1
ログを積み重ねて後から確認するには
>> が必須です。さらに 2>&1 を付けることで、コマンドが失敗したときのエラーメッセージも記録できます。>> log.txt 2>&1 をひとつの定型句として覚えておきましょう。2. タイムスタンプ付きログメッセージの書き方
ログにメッセージを書くとき、「いつ」その行が出力されたかがわかると後から問題を追跡しやすくなります。%DATE% と %TIME% を組み合わせてタイムスタンプを先頭に付けましょう。
2-1. echo でタイムスタンプを先頭に付ける
@echo off :: タイムスタンプを先頭に付けてログ出力 echo [%DATE% %TIME%] 処理を開始します >> log.txt echo [%DATE% %TIME%] ファイルをコピーしています >> log.txt xcopy "C:\work\data" "C:\backup" /e /y >> log.txt 2>&1 echo [%DATE% %TIME%] 処理が完了しました >> log.txt
出力例(%DATE% は環境により形式が異なります):
[2025/04/14 9:03:21.50] 処理を開始します [2025/04/14 9:03:21.62] ファイルをコピーしています [2025/04/14 9:03:22.18] 処理が完了しました
2-2. タイムスタンプをログ用変数にまとめる(推奨)
毎行 %DATE% %TIME% と書くのは冗長です。スクリプト先頭でタイムスタンプ変数を定義しておくと管理しやすくなります。
@echo off
setlocal enabledelayedexpansion
:: ─── ログ出力用サブルーチン ────────────────────────
:log
set "_now=%DATE% %TIME%"
echo [!_now!] %~1 >> "%LOG_FILE%"
echo [!_now!] %~1
goto :eof
:: ─── メイン処理 ─────────────────────────────────────
set "LOG_FILE=%~dp0logs\process_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.txt"
call :log "処理を開始します"
call :log "ステップ1: データ確認"
call :log "ステップ2: ファイルコピー"
call :log "処理が完了しました"
9時台の
%TIME% は 9:03:21.50 のように先頭にスペースが入ります。ファイル名やソート用途では %TIME: =0% とすることでスペースを 0 に置換できます(例: 09:03:21.50)。ログメッセージへの埋め込みなら見た目の違いは気にしなくて問題ありません。3. 日付付きログファイル名の作り方
毎日実行するスクリプトでは、ログファイルを日付ごとに分けるのが一般的です。%DATE% からスラッシュを除いてファイル名に使いましょう。日付と時刻をファイル名に使う方法の完全ガイドも参照してください。
@echo off :: ─── 日付をファイル名に使える形式に変換 ────────── :: %DATE% が「2025/04/14」形式の場合 set "YMD=%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%" :: ─── ログファイルのパスを定義 ───────────────────── set "LOG_DIR=%~dp0logs" set "LOG_FILE=%LOG_DIR%\process_%YMD%.txt" :: ─── logsフォルダを自動作成 ──────────────────────── if not exist "%LOG_DIR%\" mkdir "%LOG_DIR%" :: ─── ログ出力開始 ─────────────────────────────────── echo [%DATE% %TIME%] ========== 処理開始 ========== >> "%LOG_FILE%" echo [%DATE% %TIME%] YMD=%YMD% >> "%LOG_FILE%" echo [%DATE% %TIME%] ========== 処理終了 ========== >> "%LOG_FILE%" echo ログ出力先: %LOG_FILE% pause
上記の実行結果として logs\process_20250414.txt が作成されます。日付ごとにファイルが分かれるため、ログを日付付きフォルダに保存する方法と組み合わせるとさらに整理しやすくなります。
4. PC名・ユーザー名・IP アドレスをログに含める
複数台のPCで同じバッチを実行する場合や、誰が実行したか記録したい場合は、PC名やユーザー名をログに含めましょう。
4-1. PC名とユーザー名を記録する
%COMPUTERNAME% でPC名、%USERNAME% でログインユーザー名を取得できます。詳しくは コンピューター名を取得する方法・ログインユーザー名を取得する方法を参照してください。
@echo off set "LOG_FILE=%~dp0logs\process_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.txt" if not exist "%~dp0logs\" mkdir "%~dp0logs" :: ─── ヘッダー情報をログに記録 ───────────────────── echo ===== 実行情報 ===== >> "%LOG_FILE%" echo 実行日時 : %DATE% %TIME% >> "%LOG_FILE%" echo PC名 : %COMPUTERNAME% >> "%LOG_FILE%" echo ユーザー名 : %USERNAME% >> "%LOG_FILE%" echo スクリプト : %~f0 >> "%LOG_FILE%" echo ==================== >> "%LOG_FILE%" :: ─── 以降に実際の処理を記述 ──────────────────────── echo [%DATE% %TIME%] 処理を開始します >> "%LOG_FILE%" pause
4-2. IPアドレスもログに記録する
ネットワーク環境の記録にはIPアドレスも有用です。ipconfig の出力から for /f でIPを抽出してログに書き込みます。IPアドレスを取得してログに記録する方法も参照してください。
@echo off
setlocal enabledelayedexpansion
:: for /f で ipconfig からIPv4アドレスを抽出
for /f "tokens=2 delims=:" %%i in ('ipconfig ^| findstr /i "IPv4"') do (
set "IP_ADDR=%%i"
:: 先頭スペースを除去
set "IP_ADDR=!IP_ADDR: =!"
goto :got_ip
)
:got_ip
:: ─── 取得したIPをログに書き込む ────────────────────
echo IPアドレス : !IP_ADDR! >> "%LOG_FILE%"
5. カッコブロックで複数コマンドをまとめてログに記録する
複数のコマンドの出力をまとめて1つのログファイルに書き込むとき、カッコブロック ( ... ) >> log.txt 2>&1 を使うと、各コマンドへの >> 記述を省略できます。
@echo off
set "LOG_FILE=%~dp0logs\check_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.txt"
if not exist "%~dp0logs\" mkdir "%~dp0logs"
:: ─── カッコブロックで一括ログ出力 ─────────────────
(
echo ===== システム確認レポート =====
echo 実行日時: %DATE% %TIME%
echo PC名: %COMPUTERNAME%
echo ユーザー: %USERNAME%
echo.
echo --- ディスク使用量 ---
dir C:\ /s /-c 2>&1 | findstr "バイト"
echo.
echo --- ネットワーク接続 ---
ipconfig | findstr /i "ipv4"
echo.
echo ===== 確認完了 =====
) >> "%LOG_FILE%" 2>&1
echo ログ出力完了: %LOG_FILE%
pause
カッコブロックを使うと、ブロック全体を1つのリダイレクトで処理できます。行末ごとに
>> log.txt 2>&1 を書く必要がなくなるため、可読性が上がり、ファイル名の指定ミスも減ります。長い処理をまとめてログに落とす場合はこのパターンが最適です。6. ログを読みやすくする:区切り線・レベル分け・画面同時出力
6-1. 区切り線とセクション見出しを入れる
ログファイルが長くなると読みにくくなります。区切り線や見出しを入れると後から確認しやすくなります。
@echo off set "LOG_FILE=%~dp0logs\process_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.txt" if not exist "%~dp0logs\" mkdir "%~dp0logs" :: ─── セパレーター行 ───────────────────────────────── echo. >> "%LOG_FILE%" echo ============================================================ >> "%LOG_FILE%" echo 実行開始: %DATE% %TIME% >> "%LOG_FILE%" echo スクリプト: %~nx0 >> "%LOG_FILE%" echo ============================================================ >> "%LOG_FILE%" :: ─── 処理ステップ ─────────────────────────────────── echo [INFO ] %DATE% %TIME% ステップ1: データ検証中 >> "%LOG_FILE%" echo [INFO ] %DATE% %TIME% ステップ2: ファイルコピー中 >> "%LOG_FILE%" echo [WARN ] %DATE% %TIME% 対象ファイルが0件でした >> "%LOG_FILE%" echo [ERROR] %DATE% %TIME% コピー失敗: アクセス拒否 >> "%LOG_FILE%" :: ─── セパレーター(終了) ────────────────────────── echo ============================================================ >> "%LOG_FILE%" echo 実行終了: %DATE% %TIME% >> "%LOG_FILE%" echo ============================================================ >> "%LOG_FILE%" pause
6-2. ログファイルと画面に同時出力する(tee の代替)
バッチファイルには Linux の tee コマンドがないため、標準の方法ではファイルに書くと画面に出なくなります。ログファイルと画面への同時出力は、echo を2回書くシンプルな方法が確実です。
@echo off
set "LOG_FILE=%~dp0logs\process.txt"
if not exist "%~dp0logs\" mkdir "%~dp0logs"
:: ─── 画面とファイルの両方に出力するサブルーチン ──
:tee_log
echo [%DATE% %TIME%] %~1
echo [%DATE% %TIME%] %~1 >> "%LOG_FILE%"
goto :eof
:: ─── 使い方 ─────────────────────────────────────────
call :tee_log "処理を開始します"
call :tee_log "ファイルをコピーしています"
call :tee_log "処理が完了しました"
pause
7. コマンドの成否(ERRORLEVEL)をログに記録する
処理の成否をログに記録しておくと、バッチが途中で失敗したときの原因特定に役立ちます。%ERRORLEVEL% でコマンドの終了コードを取得できます。
@echo off
setlocal enabledelayedexpansion
set "LOG_FILE=%~dp0logs\process_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.txt"
if not exist "%~dp0logs\" mkdir "%~dp0logs"
:: ─── コマンドを実行してERRORLEVELをログに記録 ────
xcopy "C:\work\data" "C:\backup\data" /e /y >> "%LOG_FILE%" 2>&1
set "RC=!ERRORLEVEL!"
if "!RC!"=="0" (
echo [%DATE% %TIME%] [OK ] xcopy 成功 (ERRORLEVEL=!RC!) >> "%LOG_FILE%"
echo [OK] xcopy 完了
) else (
echo [%DATE% %TIME%] [ERROR] xcopy 失敗 (ERRORLEVEL=!RC!) >> "%LOG_FILE%"
echo [ERROR] xcopy 失敗。ログを確認してください: %LOG_FILE%
exit /b !RC!
)
pause
IF ブロック内で
%ERRORLEVEL% を使うと、ブロック解析時の値(常に0)が展開されてしまいます。setlocal enabledelayedexpansion を宣言し、!ERRORLEVEL! と ! で囲んで使うことで、コマンド実行直後の正確な値を参照できます。詳しくは 遅延展開の完全ガイドを参照してください。8. 古いログファイルを自動削除してログを管理する
ログファイルが蓄積すると容量を圧迫します。forfiles コマンドを使って、一定日数以上前のログを自動削除しましょう。ログローテーションの完全ガイドも参照してください。
@echo off
set "LOG_DIR=%~dp0logs"
:: ─── 30日以上前の .txt ログを削除 ──────────────────
if exist "%LOG_DIR%\" (
forfiles /p "%LOG_DIR%" /m "*.txt" /d -30 /c "cmd /c del @file" >nul 2>&1
echo [%DATE% %TIME%] 30日以上前のログを削除しました >> "%LOG_DIR%\cleanup.log"
)
:: ─── 以降に通常の処理を記述 ────────────────────────
set "LOG_FILE=%LOG_DIR%\process_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.txt"
echo [%DATE% %TIME%] 処理を開始します >> "%LOG_FILE%"
pause
forfiles /d -30 は「更新日時が30日以上前のファイル」を対象にします。/d -7 なら7日以上前、/d -90 なら90日以上前です。バッチの実行頻度や保存要件に合わせて数値を調整してください。forfiles は対象ファイルが存在しない場合にエラーコードを返しますが、>nul 2>&1 で無視できます。9. 実践例3本
実践例1:処理結果を日次ログファイルに記録する汎用スクリプト
毎日実行するバッチ処理の基本テンプレートです。実行情報ヘッダー・処理ステップ・終了コード記録・古いログ削除を組み合わせています。
@echo off
setlocal enabledelayedexpansion
:: ─── 設定 ───────────────────────────────────────────
set "LOG_DIR=%~dp0logs"
set "YMD=%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%"
set "LOG_FILE=%LOG_DIR%\daily_%YMD%.txt"
:: ─── logsフォルダ作成 & 古いログ削除(30日以上) ──
if not exist "%LOG_DIR%\" mkdir "%LOG_DIR%"
forfiles /p "%LOG_DIR%" /m "daily_*.txt" /d -30 /c "cmd /c del @file" >nul 2>&1
:: ─── ヘッダー ──────────────────────────────────────
(
echo.
echo ============================================================
echo スクリプト : %~nx0
echo 開始日時 : %DATE% %TIME%
echo PC名 : %COMPUTERNAME%
echo ユーザー : %USERNAME%
echo ============================================================
) >> "%LOG_FILE%"
:: ─── 処理本体 ──────────────────────────────────────
echo [%DATE% %TIME%] [INFO] ステップ1: データコピー開始 >> "%LOG_FILE%"
xcopy "C:\work\source" "C:\work\backup" /e /y >> "%LOG_FILE%" 2>&1
set "RC=!ERRORLEVEL!"
if "!RC!"=="0" (
echo [%DATE% %TIME%] [OK ] xcopy 成功 >> "%LOG_FILE%"
) else (
echo [%DATE% %TIME%] [ERR ] xcopy 失敗 ERRORLEVEL=!RC! >> "%LOG_FILE%"
goto :end
)
:: ─── フッター ──────────────────────────────────────
:end
(
echo 終了日時 : %DATE% %TIME%
echo ============================================================
) >> "%LOG_FILE%"
echo 完了。ログ: %LOG_FILE%
pause
実践例2:複数フォルダのファイル一覧をまとめてログに記録する
複数のフォルダを定期的に棚卸しするための監査ログ作成スクリプトです。カッコブロックで各フォルダの情報をまとめて1つのファイルに出力します。
@echo off
set "LOG_DIR=%~dp0audit_logs"
set "LOG_FILE=%LOG_DIR%\audit_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.txt"
if not exist "%LOG_DIR%\" mkdir "%LOG_DIR%"
:: ─── 監査対象フォルダ ───────────────────────────────
set "TARGETS=C:\work C:\data C:\reports"
(
echo ===== フォルダ監査レポート =====
echo 実行日時: %DATE% %TIME%
echo PC: %COMPUTERNAME% / User: %USERNAME%
echo.
) >> "%LOG_FILE%"
for %%D in (%TARGETS%) do (
(
echo --- %%D ---
if exist "%%D\" (
dir "%%D" /a /-c 2>&1
) else (
echo [警告] フォルダが存在しません: %%D
)
echo.
) >> "%LOG_FILE%" 2>&1
)
echo 監査ログ出力完了: %LOG_FILE%
pause
実践例3:エラー発生時のみ別ログに分けて記録する
通常ログとエラーログを分離して保存するパターンです。エラーログが空なら正常終了、ファイルがあれば何かしら問題が起きたとすぐわかります。
@echo off
setlocal enabledelayedexpansion
set "LOG_DIR=%~dp0logs"
set "YMD=%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%"
set "LOG_FILE=%LOG_DIR%\process_%YMD%.txt"
set "ERR_FILE=%LOG_DIR%\error_%YMD%.txt"
if not exist "%LOG_DIR%\" mkdir "%LOG_DIR%"
:: ─── 処理リスト ─────────────────────────────────────
set /a "TOTAL=0"
set /a "ERRORS=0"
for %%F in ("C:\work\*.csv") do (
set /a "TOTAL+=1"
echo [%DATE% %TIME%] [INFO] 処理中: %%~nxF >> "%LOG_FILE%"
:: ここで実際の処理(例: copy)
copy "%%F" "C:\backup\%%~nxF" >nul 2>&1
set "RC=!ERRORLEVEL!"
if "!RC!"=="0" (
echo [%DATE% %TIME%] [OK ] %%~nxF >> "%LOG_FILE%"
) else (
echo [%DATE% %TIME%] [ERR ] %%~nxF ERRORLEVEL=!RC! >> "%LOG_FILE%"
echo [%DATE% %TIME%] [ERR ] %%~nxF ERRORLEVEL=!RC! >> "%ERR_FILE%"
set /a "ERRORS+=1"
)
)
:: ─── サマリー出力 ───────────────────────────────────
echo [%DATE% %TIME%] ===== 完了: TOTAL=!TOTAL! ERRORS=!ERRORS! ===== >> "%LOG_FILE%"
if !ERRORS! GTR 0 (
echo [警告] !ERRORS! 件のエラーが発生しました。エラーログ: %ERR_FILE%
) else (
echo [完了] 全 !TOTAL! 件正常終了
)
pause
10. まとめ:ログ出力チートシート
| やりたいこと | 記述例 | 備考 |
|---|---|---|
| 出力を上書き保存 | echo メッセージ > log.txt |
毎回新規作成 |
| 出力を追記保存 | echo メッセージ >> log.txt |
実務の基本形 |
| エラーも一緒に記録 | コマンド >> log.txt 2>&1 |
2>&1 を必ず付ける |
| タイムスタンプ付き出力 | echo [%DATE% %TIME%] メッセージ >> log.txt |
日付・時刻を先頭に |
| 日付付きファイル名 | set "YMD=%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%" |
スラッシュを除く |
| PC名・ユーザー名を記録 | echo %COMPUTERNAME% %USERNAME% >> log.txt |
実行者特定に有用 |
| 複数コマンドをまとめて出力 | ( コマンド群 ) >> log.txt 2>&1 |
各行への >> が不要に |
| ERRORLEVELを記録 | set "RC=!ERRORLEVEL!" & echo RC=!RC! >> log.txt |
setlocal enabledelayedexpansion 必要 |
| 画面とファイルに同時出力 | echo を2回(画面用と追記用) | tee の代替 |
| 古いログを自動削除 | forfiles /p logdir /m *.txt /d -30 /c "cmd /c del @file" |
30日以上前を削除 |
FAQ
Qログファイルにコマンドの出力と echo のメッセージを混在させたい。
Aどちらも >> log.txt 2>&1 でリダイレクトすれば、ファイルに書き込んだ順に並びます。カッコブロック ( ... ) >> log.txt 2>&1 を使うと、echo とコマンドを混在させてもすべてをひとつの記述でまとめられます。
Qログファイルが大きくなりすぎて困っています。
A2つの対策があります。①forfiles /d -N でN日以上前のログを定期削除する、②日付付きファイル名にして日次・週次でログファイルを分割する。ログローテーションの完全ガイドも参照してください。
Qバッチが途中で止まるとログの最後の行が書かれないことがあります。
Aecho によるリダイレクトはコマンド実行ごとにファイルへ書き込まれるため、途中終了しても書き込み済みの行は残ります。「処理完了」の行がログに残っていなければ、その前の行が最後に実行されたコマンドです。ステップごとに echo [%DATE% %TIME%] ステップX開始 >> log.txt を入れておくと、どこで止まったかが一目でわかります。
Qログファイルのパスにスペースが含まれるとエラーになります。
Aset "LOG_FILE=C:\My Folder\log.txt" のようにスペースを含む場合、リダイレクト時は >> "%LOG_FILE%" のようにダブルクォートで囲む必要があります。変数にパスを格納するときも set "変数=値" 形式を使い、展開時は常に "%LOG_FILE%" とクォートする習慣をつけましょう。
Q複数のログファイルから特定のキーワードを一括検索したい。
Afindstr /s "エラー" "C:\logs\*.txt" で再帰検索できます。複数のログファイルを一括で検索する方法に詳しい使い方があります。
Qバッチを画面に表示せずにサイレント実行してログだけに出力したい。
Aスクリプト先頭の @echo off に加えて、各コマンドの末尾に >nul 2>&1 を付けるとコマンドの出力が画面に出なくなります。ただしデバッグ時は >> log.txt 2>&1 に切り替えて実行内容を確認できるようにしておくのが安全です。

