【bat】バッチファイルでログを解析・分岐する完全ガイド|行数カウント・キーワード検索・エラー件数・複数条件判定まで徹底解説

【bat】バッチファイルでログを解析・分岐する完全ガイド|行数カウント・キーワード検索・エラー件数・複数条件判定まで徹底解説 bat

バッチファイルによる自動処理では、ログファイルの内容を判定して次の処理を切り替える場面が頻繁にあります。「ERRORが含まれていれば担当者にメールを送る」「行数が5000行を超えたらログをローテーションする」「警告件数が10件以上なら処理を中断する」といった制御がバッチファイルだけで実現できます。

この記事では、行数カウントの複数手法・findstrによるキーワード検索・エラー件数の集計・AND/OR複合条件・特定行の抽出まで、実務で使えるパターンを体系的に解説します。FINDSTRコマンドの使い方完全ガイドも合わせて読むと、findstrの全オプションを把握できます。

この記事で解説すること
行数カウントの3つの方法(find /v “”・for /f・PowerShell連携)、行数による条件分岐とログローテーション連携、findstrでのキーワード存在確認と件数カウント(find /c)、複数キーワードのAND/OR判定・正規表現検索、最終行・最新エラー行の抽出、実践的な自動監視スクリプトの実装例まで網羅
スポンサーリンク

ログファイルの行数をカウントする方法

バッチファイルで行数を取得するには主に3つの方法があります。用途と精度に応じて使い分けましょう。

方法1:find /c “” で高速カウント(推奨)

find /c "" は空文字列にマッチする行をカウントします。空行も含む全行数を取得でき、最も高速でシンプルな方法です。

行数カウント(find /c 方式)
@echo off
setlocal enabledelayedexpansion

set "LOGFILE=C:\logs\app.log"

:: find /c "" で行数を含む文字列を取得 → 数値だけ切り出す
for /f "tokens=3" %%N in ('find /c "" "%LOGFILE%"') do (
    set /a LINE_COUNT=%%N
)

echo 行数: !LINE_COUNT! 行

:: 閾値での分岐
if !LINE_COUNT! GTR 5000 (
    echo [WARNING] 5000行を超えました。ログローテーションを実行します。
    :: call :rotate_log
) else (
    echo [OK] 行数は正常範囲内です。
)

endlocal
find /c “” の出力形式
find /c "" ファイル名 を実行すると、---------- C:\logs\app.log: 1234 という形式で出力されます。for /f "tokens=3" で3番目のトークン(行数の数値部分)を切り出しています。

方法2:for /f でループカウント(行ごとに処理したいとき)

ログの各行を読み取りながら同時にカウントしたい場合は for /f 方式を使います。行内容にアクセスしながらカウントできるのが利点です。

for /f によるカウント(行内容も取得可能)
@echo off
setlocal enabledelayedexpansion

set "LOGFILE=C:\logs\app.log"
set /a LINE_COUNT=0
set /a ERROR_COUNT=0

for /f "usebackq delims=" %%L in ("%LOGFILE%") do (
    set /a LINE_COUNT+=1
    :: 各行にERRORが含まれているかチェックしながらカウント
    echo %%L | findstr /i "ERROR" > nul
    if not errorlevel 1 set /a ERROR_COUNT+=1
)

echo 総行数   : !LINE_COUNT! 行
echo エラー行数: !ERROR_COUNT! 件

endlocal
for /f は空行をスキップする
for /f空行をスキップする仕様です。空行を含む正確な行数が必要な場合は find /c "" を使ってください。空行もカウントしたい場合は for /f "delims=" でも空行はスキップされるため、find /c 方式が確実です。

方法3:PowerShell を呼び出す(Unicode対応・大ファイル向け)

UTF-8やUnicode形式のログ、数百MBの大容量ログには PowerShell を使う方が確実です。

PowerShell で行数取得(文字コード対応)
@echo off
setlocal

set "LOGFILE=C:\logs\unicode_app.log"

:: PowerShell で行数を取得(UTF-8含む任意エンコーディング対応)
for /f %%N in ('powershell -NoProfile -Command "(Get-Content -Path '%LOGFILE%' -Encoding UTF8).Count"') do (
    set /a LINE_COUNT=%%N
)

echo 行数: %LINE_COUNT% 行

endlocal
方法 速度 空行カウント 文字コード対応 用途
find /c "" ◎ 最速 ○ 含む △ Shift-JIS前提 最もシンプルな行数取得
for /f ループ ○ 普通 ✕ スキップ △ Shift-JIS前提 行ごとに処理しながらカウント
PowerShell ○ 普通 ○ 含む ◎ 指定可能 Unicode・大容量ログ対応

行数に応じた多段階の条件分岐

取得した行数を使って処理を多段階に分岐できます。バッチファイルで条件分岐する方法完全ガイドと組み合わせることで、複雑な制御フローも実装できます。

行数による多段階分岐パターン
@echo off
setlocal enabledelayedexpansion

set "LOGFILE=C:\logs\app.log"
set /a WARN_LINE=1000
set /a ROTATE_LINE=5000
set /a CRITICAL_LINE=10000

:: 行数取得
for /f "tokens=3" %%N in ('find /c "" "%LOGFILE%"') do set /a LINES=%%N

echo ログ行数: !LINES! 行

if !LINES! GEQ %CRITICAL_LINE% (
    echo [CRITICAL] 行数が%CRITICAL_LINE%行を超えました。緊急ローテーションします。
    call :do_rotate
    call :send_alert CRITICAL !LINES!
    goto :end
)

if !LINES! GEQ %ROTATE_LINE% (
    echo [WARN] 行数が%ROTATE_LINE%行を超えました。ローテーションします。
    call :do_rotate
    goto :end
)

if !LINES! GEQ %WARN_LINE% (
    echo [INFO] 行数が%WARN_LINE%行を超えました。要監視状態です。
    goto :end
)

echo [OK] 行数は正常範囲内です。

:end
endlocal
exit /b 0

:do_rotate
echo ログローテーション実行中...
:: ここにローテーション処理を記述
exit /b 0

:send_alert
echo アラート送信: %1 - 行数 %2
exit /b 0
GEQ vs GTR の使い分け
GTR は「より大きい(超える)」、GEQ は「以上(含む)」です。「5000行を超えたら」は GTR 5000、「5000行以上なら」は GEQ 5000 を使います。数値比較の詳細はバッチファイルで条件分岐する方法完全ガイドを参照してください。

findstr によるキーワード検索と存在確認

findstr はバッチファイルでの文字列検索の基本コマンドです。ログにキーワードが含まれているかの判定から、マッチ行の件数カウントまで幅広く使えます。

キーワードの存在確認(0件か1件以上か)

基本的なキーワード存在確認
@echo off
setlocal

set "LOGFILE=C:\logs\app.log"
set "KEYWORD=ERROR"

:: findstr でキーワードを検索(結果は nul へ捨てる)
findstr /i "%KEYWORD%" "%LOGFILE%" > nul 2>&1

if errorlevel 1 (
    echo [OK] "%KEYWORD%" は見つかりませんでした。
) else (
    echo [NG] "%KEYWORD%" が検出されました。エラー対応処理を実行します。
    :: call :handle_error
)

endlocal

よく使う findstr オプション

オプション 説明
/i 大文字・小文字を区別しない findstr /i "error"(ERROR・Error・error すべて対象)
/c:"文字列" スペースを含むフレーズを検索 findstr /c:"disk full"
/v キーワードを含まない行を抽出 findstr /v "OK"(OK以外の行を出力)
/n 行番号付きで出力 findstr /n "ERROR"(3:ERROR発生 のように表示)
/r 正規表現検索 findstr /r "ERR[0-9][0-9]"
/s サブフォルダも再帰検索 findstr /s "ERROR" C:\logs\*.log
/l リテラル検索(正規表現を無効化) findstr /l "a.b"(a.b のみ・a任意文字bにはマッチしない)
findstrの全オプション
FINDSTRコマンドの使い方完全ガイドでは、findstrの全オプション・正規表現パターン・複数ファイル横断検索など、実務で役立つ使い方を網羅しています。

キーワードの出現件数をカウントして閾値判定

「ERRORが1件でもあれば」ではなく「ERRORが10件以上なら緊急対応」という判定には、find /c コマンドでキーワードの出現行数をカウントします。

エラー件数のカウントと閾値判定
@echo off
setlocal enabledelayedexpansion

set "LOGFILE=C:\logs\app.log"
set "KEYWORD=ERROR"
set /a WARN_COUNT=5
set /a CRIT_COUNT=20

:: find /c でキーワードを含む行数をカウント
for /f "tokens=3" %%N in ('find /c /i "%KEYWORD%" "%LOGFILE%"') do (
    set /a ERR_COUNT=%%N
)

echo ERRORの件数: !ERR_COUNT! 件

if !ERR_COUNT! GEQ %CRIT_COUNT% (
    echo [CRITICAL] ERROR が %CRIT_COUNT% 件以上。緊急対応が必要です。
    call :send_mail CRITICAL !ERR_COUNT!
    exit /b 1
)

if !ERR_COUNT! GEQ %WARN_COUNT% (
    echo [WARNING] ERROR が %WARN_COUNT% 件以上です。
    call :send_mail WARNING !ERR_COUNT!
    exit /b 0
)

if !ERR_COUNT! GTR 0 (
    echo [INFO] ERROR が !ERR_COUNT! 件あります。
    exit /b 0
)

echo [OK] エラーなし
exit /b 0

:send_mail
echo メール送信: レベル=%1 件数=%2
exit /b 0

endlocal

複数のキーワードをそれぞれカウント

複数キーワードを個別にカウントする
@echo off
setlocal enabledelayedexpansion

set "LOGFILE=C:\logs\app.log"

:: 各キーワードの件数を取得
for /f "tokens=3" %%N in ('find /c /i "ERROR"   "%LOGFILE%"') do set /a CNT_ERROR=%%N
for /f "tokens=3" %%N in ('find /c /i "WARNING" "%LOGFILE%"') do set /a CNT_WARN=%%N
for /f "tokens=3" %%N in ('find /c /i "FATAL"   "%LOGFILE%"') do set /a CNT_FATAL=%%N

echo ── ログ解析結果 ──────────────────────
echo  FATAL  : !CNT_FATAL! 件
echo  ERROR  : !CNT_ERROR! 件
echo  WARNING: !CNT_WARN!  件

:: FATAL が1件でもあれば最優先対応
if !CNT_FATAL! GTR 0 (
    echo [CRITICAL] FATALエラーを検出しました。即時対応してください。
    exit /b 2
)

:: ERROR が10件以上なら警告
if !CNT_ERROR! GEQ 10 (
    echo [ERROR] エラーが多発しています。
    exit /b 1
)

echo [OK] 重大なエラーはありません。

endlocal

複数キーワードの AND・OR 複合判定

実務では「ERRORを含み、かつRETRYを含まない行」「WARNINGまたはERRORを含む行」といった複合条件が必要になることがあります。

AND 条件:すべてのキーワードを含む判定

AND条件(パイプで findstr をつなぐ)
@echo off
setlocal

set "LOGFILE=C:\logs\app.log"

:: "ERROR" かつ "DATABASE" を含む行があるか(パイプで絞り込み)
findstr /i "ERROR" "%LOGFILE%" | findstr /i "DATABASE" > nul 2>&1

if errorlevel 1 (
    echo DB関連のERRORは見つかりませんでした。
) else (
    echo [NG] DBに関連するERRORを検出しました。
)

endlocal

OR 条件:いずれかのキーワードを含む判定

OR条件(findstr の複数パターン指定)
@echo off
setlocal

set "LOGFILE=C:\logs\app.log"

:: "ERROR" または "FATAL" または "CRITICAL" のいずれかを含む行
findstr /i "ERROR FATAL CRITICAL" "%LOGFILE%" > nul 2>&1

if errorlevel 1 (
    echo 重大なキーワードは見つかりませんでした。
) else (
    echo [NG] 重大なエラーを検出しました。
)

endlocal
findstr の複数パターンはスペース区切り OR(AND ではない)
findstr "ERROR FATAL" は「ERRORまたはFATAL」を含む行を検索します(OR条件)。スペースを含むフレーズを検索する場合は /c:"disk full" のように /c: オプションを使ってください。

NOT 条件:キーワードを含まない行を対象にする

NOT条件(/v オプションで否定)
@echo off
setlocal

set "LOGFILE=C:\logs\app.log"

:: "OK" を含まない行(= 問題がある可能性の行)をカウント
for /f "tokens=3" %%N in ('findstr /v /i "OK" "%LOGFILE%" ^| find /c ""') do (
    set /a NOT_OK_COUNT=%%N
)

echo OK以外の行数: %NOT_OK_COUNT% 件

:: "ERROR" を含むが "RETRY成功" を含まない行
findstr /i "ERROR" "%LOGFILE%" | findstr /v /i "RETRY成功" > nul 2>&1
if errorlevel 1 (
    echo リカバリ不能なERRORはありません。
) else (
    echo [NG] リトライでも解消されていないERRORがあります。
)

endlocal

正規表現による高度なパターン検索

正規表現でパターンマッチ(/r オプション)
@echo off
setlocal

set "LOGFILE=C:\logs\app.log"

:: ERR + 4桁数字のエラーコードパターンを検索(例: ERR1023・ERR5502)
findstr /r /i "ERR[0-9][0-9][0-9][0-9]" "%LOGFILE%" > nul 2>&1
if not errorlevel 1 echo エラーコードパターンを検出しました。

:: 特定の日時形式を含む行を検索(2024-xx-xx 形式)
findstr /r "2[0-9][0-9][0-9]-[01][0-9]-[0-3][0-9]" "%LOGFILE%" > nul 2>&1
if not errorlevel 1 echo 日付形式の行を検出しました。

:: IPアドレスのパターンを検索
findstr /r "[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*" "%LOGFILE%" > nul 2>&1
if not errorlevel 1 echo IPアドレスを含む行を検出しました。

endlocal
findstr の正規表現はJavaや.NETとは異なる
findstrの正規表現(/r)はPOSIX風の独自実装で、一般的な正規表現と異なる点があります。+(1回以上)や \d(数字)は使えません。複雑な正規表現が必要な場合は PowerShell の Select-String を使う方が確実です。

特定行の抽出:最終行・先頭行・直近Nエラー行

エラーが発生した際、「最後に出力されたエラー内容」だけを取得してメール通知に含めるといった用途で、特定行の抽出が役立ちます。

最終行だけを取得する

最終行の取得
@echo off
setlocal enabledelayedexpansion

set "LOGFILE=C:\logs\app.log"
set "LAST_LINE="

:: for /f でファイルを最後まで読み、最後の値が最終行になる
for /f "usebackq delims=" %%L in ("%LOGFILE%") do (
    set "LAST_LINE=%%L"
)

echo 最終行: !LAST_LINE!

endlocal

最新のエラー行だけを取得する

最新エラー行の取得
@echo off
setlocal enabledelayedexpansion

set "LOGFILE=C:\logs\app.log"
set "LAST_ERROR="

:: ERRORを含む行を順次読み込み、最後のものが最新エラー
for /f "usebackq delims=" %%L in ('findstr /i "ERROR" "%LOGFILE%"') do (
    set "LAST_ERROR=%%L"
)

if defined LAST_ERROR (
    echo 最新のエラー行:
    echo !LAST_ERROR!
) else (
    echo エラーは見つかりませんでした。
)

endlocal

直近N件のエラー行をファイルに保存する

直近Nエラー行をファイルに抽出(PowerShell利用)
@echo off
setlocal

set "LOGFILE=C:\logs\app.log"
set "OUTPUT=C:\logs\recent_errors.txt"
set /a N=20

:: PowerShell で直近20件のERROR行を取得
powershell -NoProfile -Command ^
    "Get-Content '%LOGFILE%' | Select-String 'ERROR' | Select-Object -Last %N% | Out-File '%OUTPUT%' -Encoding UTF8"

echo 直近%N%件のエラーを %OUTPUT% に保存しました。

endlocal

特定日時以降のエラーだけを判定する

当日分のエラーだけをカウントする
@echo off
setlocal enabledelayedexpansion

set "LOGFILE=C:\logs\app.log"

:: 今日の日付を YYYY-MM-DD 形式で取得
for /f "tokens=1,2,3 delims=/" %%a in ("%DATE%") do (
    set "TODAY=%%a-%%b-%%c"
)
:: ※環境によって %DATE% の形式が異なるため注意(例: 2024/01/15 や 2024-01-15)
:: wmic を使う場合:
:: for /f %%D in ('wmic os get LocalDateTime ^| findstr "^[0-9]"') do set "DT=%%D"
:: set "TODAY=!DT:~0,4!-!DT:~4,2!-!DT:~6,2!"

:: 今日の日付を含むERROR行をカウント
for /f "tokens=3" %%N in ('findstr /i "%TODAY%" "%LOGFILE%" ^| find /c /i "ERROR"') do (
    set /a TODAY_ERRORS=%%N
)

echo 本日 (%TODAY%) のERROR件数: !TODAY_ERRORS! 件

endlocal

実践パターン:完全自動ログ監視スクリプト

行数チェック・キーワード件数判定・通知・ログ退避をすべて組み合わせた、タスクスケジューラで定期実行できる完成版スクリプトです。ログファイルを監視して自動処理するバッチファイル完全ガイドも合わせて参照すると、より高度な監視パターンを学べます。

log_monitor.bat(完成版)
@echo off
setlocal enabledelayedexpansion

:: ============================================================
:: log_monitor.bat - ログファイル自動監視スクリプト
:: タスクスケジューラで5分ごとに実行する想定
:: ============================================================

:: ── 設定 ────────────────────────────────────────────────
set "LOGFILE=C:\logs\app.log"
set "REPORT_DIR=C:\logs\reports"
set "ARCHIVE_DIR=C:\logs\archive"
set "MONITOR_LOG=C:\logs\monitor.log"

:: 閾値設定
set /a LINE_WARN=5000
set /a LINE_ROTATE=10000
set /a ERR_WARN=5
set /a ERR_CRIT=20

:: ── 初期化 ──────────────────────────────────────────────
if not exist "%REPORT_DIR%" mkdir "%REPORT_DIR%"
if not exist "%ARCHIVE_DIR%" mkdir "%ARCHIVE_DIR%"

set "NOW=%DATE% %TIME%"
echo [%NOW%] 監視開始 >> "%MONITOR_LOG%"

:: ── ログファイルの存在確認 ──────────────────────────────
if not exist "%LOGFILE%" (
    echo [%NOW%] [ERROR] ログファイルが見つかりません: %LOGFILE% >> "%MONITOR_LOG%"
    exit /b 1
)

:: ── 行数チェック ─────────────────────────────────────────
for /f "tokens=3" %%N in ('find /c "" "%LOGFILE%"') do set /a LINES=%%N
echo [%NOW%] 行数: !LINES! 行 >> "%MONITOR_LOG%"

:: ── エラー件数チェック ───────────────────────────────────
for /f "tokens=3" %%N in ('find /c /i "ERROR" "%LOGFILE%"') do set /a CNT_ERR=%%N
for /f "tokens=3" %%N in ('find /c /i "FATAL" "%LOGFILE%"') do set /a CNT_FATAL=%%N
for /f "tokens=3" %%N in ('find /c /i "WARNING" "%LOGFILE%"') do set /a CNT_WARN=%%N
echo [%NOW%] FATAL:%CNT_FATAL% ERROR:!CNT_ERR! WARNING:!CNT_WARN! >> "%MONITOR_LOG%"

:: ── FATAL チェック(最優先) ─────────────────────────────
if !CNT_FATAL! GTR 0 (
    echo [%NOW%] [CRITICAL] FATAL検出: !CNT_FATAL! 件 >> "%MONITOR_LOG%"
    call :save_report CRITICAL
    call :notify_slack CRITICAL "FATAL %CNT_FATAL%件"
    exit /b 2
)

:: ── ERROR 件数による判定 ─────────────────────────────────
if !CNT_ERR! GEQ %ERR_CRIT% (
    echo [%NOW%] [CRITICAL] ERRORが%ERR_CRIT%件以上: !CNT_ERR! 件 >> "%MONITOR_LOG%"
    call :save_report ERROR_CRITICAL
    call :notify_slack CRITICAL "ERROR %ERR_CRIT%件以上"
    exit /b 1
)
if !CNT_ERR! GEQ %ERR_WARN% (
    echo [%NOW%] [WARNING] ERRORが%ERR_WARN%件以上: !CNT_ERR! 件 >> "%MONITOR_LOG%"
    call :save_report ERROR_WARNING
)

:: ── 行数による判定 ───────────────────────────────────────
if !LINES! GEQ %LINE_ROTATE% (
    echo [%NOW%] ログローテーション実行 >> "%MONITOR_LOG%"
    call :rotate_log
) else if !LINES! GEQ %LINE_WARN% (
    echo [%NOW%] [WARNING] ログが%LINE_WARN%行を超えました >> "%MONITOR_LOG%"
)

echo [%NOW%] 監視完了 >> "%MONITOR_LOG%"
exit /b 0

:: ── サブルーチン ─────────────────────────────────────────

:save_report
:: エラー情報をレポートファイルに保存
set "RPT=%REPORT_DIR%\report_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%_%TIME:~0,2%%TIME:~3,2%.txt"
echo === ログレポート: %1 === > "%RPT%"
echo 日時: %NOW% >> "%RPT%"
echo 行数: !LINES! >> "%RPT%"
echo ERROR: !CNT_ERR! FATAL: !CNT_FATAL! >> "%RPT%"
echo --- 直近エラー行 --- >> "%RPT%"
findstr /i "ERROR FATAL" "%LOGFILE%" | powershell -Command "$input | Select-Object -Last 10" >> "%RPT%"
exit /b 0

:rotate_log
:: ログをアーカイブしてクリア
set "ARCH=%ARCHIVE_DIR%\app_%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%.log"
copy "%LOGFILE%" "%ARCH%" > nul
:: ログをクリア(新しい空ファイルで上書き)
break > "%LOGFILE%"
echo [%NOW%] ローテーション完了: %ARCH% >> "%MONITOR_LOG%"
exit /b 0

:notify_slack
:: PowerShell で Slack webhook 通知(要: Webhook URL設定)
:: powershell -Command "Invoke-RestMethod -Uri 'https://hooks.slack.com/...' -Method Post -Body (@{text='[%1] %~2'} | ConvertTo-Json)"
echo [通知] %1: %~2
exit /b 0

endlocal
タスクスケジューラ設定のポイント
このスクリプトを5分ごとに実行するには、バッチファイルで実行時刻・経過時間をログに記録する完全ガイドで解説している実行時刻記録と組み合わせると、「最後にいつ正常に監視が走ったか」を追跡できます。またバッチファイルでエラー通知メールを自動送信する完全ガイドを参照すると、:notify_slackの部分をメール通知に変更できます。

ワンライナーパターン集

よく使うログ解析パターンを短いコードでまとめました。スクリプトの部品として使えます。

ログ解析パターン集(コピペ用)
@echo off
setlocal enabledelayedexpansion

set "LOG=C:\logs\app.log"

:: ─── 行数取得 ──────────────────────────────────────────
for /f "tokens=3" %%N in ('find /c "" "%LOG%"') do set /a LINES=%%N
echo 総行数: !LINES!

:: ─── キーワード件数(複数) ───────────────────────────
for /f "tokens=3" %%N in ('find /c /i "ERROR"   "%LOG%"') do set /a C_ERR=%%N
for /f "tokens=3" %%N in ('find /c /i "WARNING" "%LOG%"') do set /a C_WARN=%%N
for /f "tokens=3" %%N in ('find /c /i "INFO"    "%LOG%"') do set /a C_INFO=%%N
echo ERROR:!C_ERR! / WARNING:!C_WARN! / INFO:!C_INFO!

:: ─── ファイルサイズ取得(バイト)─────────────────────
for %%F in ("%LOG%") do set /a FSIZE=%%~zF
echo ファイルサイズ: !FSIZE! バイト

:: ─── 最終更新日時(更新が止まっていないか確認)────────
for %%F in ("%LOG%") do set "MTIME=%%~tF"
echo 最終更新: !MTIME!

:: ─── 最終行取得 ───────────────────────────────────────
for /f "usebackq delims=" %%L in ("%LOG%") do set "LAST=%%L"
echo 最終行: !LAST!

:: ─── 最新エラー行取得 ─────────────────────────────────
for /f "usebackq delims=" %%L in ('findstr /i "ERROR" "%LOG%"') do set "LASTERR=%%L"
echo 最新ERROR: !LASTERR!

:: ─── ログが空かどうか ─────────────────────────────────
if !LINES!==0 echo ログは空です。

:: ─── 特定文字列で始まる行のカウント(行頭マッチ)───────
for /f "tokens=3" %%N in ('findstr /r "^\[ERROR\]" "%LOG%" ^| find /c ""') do set /a HEAD_ERR=%%N
echo [ERROR]で始まる行: !HEAD_ERR! 件

endlocal

よくある質問(FAQ)

Qfind /c と findstr /c: の違いは何ですか?
Afind /c "キーワード" は指定した文字列を含む行数をカウントします。findstr /c:"キーワード" はスペースを含むフレーズをリテラルで検索しますが件数は出力しません。件数カウントには find /c、マッチ行の出力には findstr と使い分けるのが基本です。
Qログファイルが大きい(数百MB)と処理が遅いですが対策はありますか?
A対策は2つあります。①直近N行だけを対象にする(PowerShellで Get-Content -Tail 1000)、②ログファイルを日次でローテーションして1日分のみ監視対象にする。バッチファイルでログローテーションを実装する方法でローテーションの実装を詳しく解説しています。
Q日本語(全角文字)を含むキーワードを findstr で検索できますか?
A検索できますが、コードページ(文字コード)の一致が必要です。chcp 65001(UTF-8)または chcp 932(Shift-JIS)をスクリプト冒頭で設定し、バッチファイル自体も同じ文字コードで保存してください。ただしバッチとfindstrの日本語処理は不安定な場合があるため、確実を期すなら PowerShell の Select-String を使うことを推奨します。
Qfor /f のループ内でカウントした変数が 0 のままになります
A遅延展開(setlocal enabledelayedexpansion)が必要です。ループ内では %VAR% ではなく !VAR! で変数を参照してください。設定を有効にしてもうまくいかない場合、call を経由する方法もあります:call set /a CNT=%%CNT%%+1
Qfindstr の検索結果の行数をカウントする最も確実な方法は?
Afindstr "パターン" ファイル | find /c "" がもっとも確実です。findstrの出力をパイプで find /c ""(全行カウント)に渡すと、マッチした行数が得られます。例:for /f "tokens=3" %%N in ('findstr /i "ERROR" app.log ^| find /c ""') do set /a N=%%N
Qログファイルが更新されているかどうかを確認するには?
Aファイルの更新日時を取得して前回実行時と比較する方法が使えます。for %%F in ("app.log") do set "MTIME=%%~tF" で最終更新日時を取得し、フラグファイルに保存した前回値と比較することで「N分以内に更新されているか」を判定できます。詳しい実装は前回処理の状態を記録して次回に引き継ぐ方法を参照してください。

まとめ

バッチファイルによるログ解析の主要パターンをまとめます。

やりたいこと コマンド ポイント
行数をカウント find /c "" ファイル tokens=3 で数値を切り出す
キーワードの存在確認 findstr /i "KEY" ファイル > nul ERRORLEVEL 1 = 未検出
キーワード件数カウント find /c /i "KEY" ファイル tokens=3 で件数を切り出す
AND 条件 findstr "A" ファイル | findstr "B" パイプで絞り込み
OR 条件 findstr "A B C" ファイル スペース区切りで OR 検索
NOT 条件 findstr /v "KEY" ファイル 一致しない行を出力
最終行を取得 for /f ループで最後に残った値 空行はスキップされる
最新エラー行 findstr "ERROR" | for /f で最後の値 パイプ経由で絞ってから読む
関連記事
ログファイルを監視して自動処理するバッチファイル完全ガイド:ログファイルを定期監視して自動処理するパターン集。
FINDSTRコマンドの使い方完全ガイド:findstrの全オプションと正規表現の使い方。
バッチファイルでログローテーションを実装する方法:ログが肥大化したときのローテーション実装。
バッチファイルでエラー通知メールを自動送信する完全ガイド:エラー検知時のメール・Slack自動通知。
バッチファイルでエラーログを自動収集して保存する方法:エラーログを自動収集してまとめる方法。