ログファイルや一時ファイルが処理失敗やバグで0バイトのまま大量に残ることがあります。放置するとファイル数が膨らみ管理が煩雑になります。バッチファイルを使えば0バイトファイルの検索・削除・ログ記録・定期実行をまとめて自動化できます。
この記事でわかること
- %%~zF でファイルサイズ0を検出して削除する基本
- ドライランで実際に削除する前に対象ファイルを確認する方法
- サブフォルダを含めて再帰的に処理する方法(for /r)
- 特定の拡張子(.log・.tmp など)に絞って削除する方法
- 削除したファイルをログファイルに記録する方法
- forfiles コマンドを使った代替実装
- タスクスケジューラで定期自動実行する設定
0バイトファイル削除の方式比較
| 方式 | コマンド | 再帰処理 | 拡張子指定 | 特徴 |
|---|---|---|---|---|
| for + %%~zF | for %%F in (*) do if %%~zF equ 0 | ○(/r) | ○ | 標準的・最も汎用性が高い |
| forfiles | forfiles /m * /c “cmd /c if @fsize==0 del @path” | ○(/s) | ○(/m) | 古いファイル指定と組み合わせやすい |
| PowerShell | Get-ChildItem | Where-Object Length -eq 0 | Remove-Item | ○(-Recurse) | ○(-Filter) | 2GB超・詳細なフィルタに強い |
基本:カレントフォルダの0バイトファイルを削除する
削除は元に戻せません。まずドライランで対象を確認してから実行することを強く推奨します。このセクションでは安全なフロー(確認 → 削除)を示します。
基本: 0バイトファイルを削除(確認プロンプト付き)
@echo off
setlocal
set TARGET_DIR=C:\work\logs
echo ===== 削除対象(0バイトファイル)=====
set COUNT=0
for %%F in ("%TARGET_DIR%\*") do (
if %%~zF equ 0 (
echo %%~nxF
set /a COUNT+=1
)
)
if %COUNT% equ 0 (
echo 0バイトファイルは見つかりませんでした。
exit /b 0
)
echo.
echo 上記 %COUNT% 件を削除します。
set /p CONFIRM=続行しますか? [y/N]:
if /i not "%CONFIRM%"=="y" (
echo キャンセルしました。
exit /b 0
)
REM 実際に削除
for %%F in ("%TARGET_DIR%\*") do (
if %%~zF equ 0 (
del "%%F"
echo [DEL] %%~nxF
)
)
echo 削除完了: %COUNT% 件
endlocal
set /p による確認プロンプトで誤実行を防ぐ
set /p CONFIRM=... で「y」以外の入力はすべてキャンセル扱いにしています。Enterキーを押しただけでは削除が実行されません。タスクスケジューラなど自動実行の場合は確認プロンプトを省略して削除ログのみ残す実装(後述)に切り替えてください。ドライランで削除前に対象ファイルを確認する
引数 dry を付けて実行すると確認のみ行い削除しません。引数なしで本番削除します。
ドライラン対応: 引数 dry で確認のみ実行
@echo off
setlocal
REM 使い方:
REM del_zero.bat → 実際に削除
REM del_zero.bat dry → 対象確認のみ(削除しない)
set TARGET_DIR=C:\work\logs
set MODE=RUN
if /i "%1"=="dry" set MODE=DRY
echo ===== モード: %MODE% =====
set COUNT=0
for /r "%TARGET_DIR%" %%F in (*) do (
if %%~zF equ 0 (
set /a COUNT+=1
if "%MODE%"=="DRY" (
echo [DRY] %%F
) else (
del "%%F"
echo [DEL] %%F
)
)
)
echo ---
if "%MODE%"=="DRY" (
echo ドライラン完了: %COUNT% 件が削除対象です。
echo 実際に削除するには引数なしで実行してください。
) else (
echo 削除完了: %COUNT% 件
)
endlocal
サブフォルダを含めて再帰的に0バイトファイルを削除する
for /r でサブフォルダを含むすべてのファイルを再帰的に処理します。削除した件数とファイル一覧を日付付きログに記録します。
サブフォルダ再帰 + 削除ログ記録
@echo off
setlocal
set TARGET_DIR=C:\work
REM ログファイル(日付付き)
for /f "tokens=2 delims==" %%A in ('wmic os get LocalDateTime /value') do set DT=%%A
set TODAY=%DT:~0,8%
set LOGFILE=C:\logs\zero_del_%TODAY%.log
if not exist "C:\logs" mkdir "C:\logs"
echo ===== 0バイトファイル削除開始: %DATE% %TIME% ===== >> "%LOGFILE%"
echo 対象フォルダ: %TARGET_DIR% >> "%LOGFILE%"
set COUNT=0
for /r "%TARGET_DIR%" %%F in (*) do (
if %%~zF equ 0 (
del "%%F"
echo [DEL] %%F >> "%LOGFILE%"
set /a COUNT+=1
)
)
echo 削除件数: %COUNT% 件 >> "%LOGFILE%"
echo ===== 削除終了: %DATE% %TIME% ===== >> "%LOGFILE%"
echo 完了: %COUNT% 件削除しました。ログ: %LOGFILE%
endlocal
削除ログは日付別ファイルで管理する
削除ログを日付付きファイル(
削除ログを日付付きファイル(
zero_del_20250428.log)で残すことで、「いつ何を削除したか」を後から追跡できます。ログの日付ファイル管理全般はログを日付別ファイルに保存する完全ガイドを参照してください。特定の拡張子(.log・.tmp)に絞って削除する
すべてのファイルではなく、.log・.tmp などの特定拡張子の0バイトファイルのみを削除対象にします。
.log と .tmp の0バイトファイルのみ削除
@echo off
setlocal
set TARGET_DIR=C:\work
REM 削除対象の拡張子をスペース区切りで列挙
for %%E in (log tmp bak ~) do (
call :del_zero_by_ext "%TARGET_DIR%" %%E
)
echo すべての拡張子の処理が完了しました。
goto :eof
:del_zero_by_ext
REM 引数: %1=対象フォルダ, %2=拡張子
set COUNT=0
for /r %1 %%F in (*.%2) do (
if %%~zF equ 0 (
del "%%F"
set /a COUNT+=1
)
)
echo [*.%2] 削除: %COUNT% 件
goto :eof
重要なファイル拡張子を誤って削除しないよう注意
.dat・.db・.ini など、空でも意味を持つ設定ファイルや初期化ファイルが0バイトで存在することがあります。削除対象の拡張子は慎重に選択し、必ずドライランで確認してから実行してください。forfiles コマンドを使った実装
forfiles は for と異なり、日付・サイズ・ファイル名パターンなどの条件をオプションで直接指定できます。「〇日以上前かつ0バイトのファイルを削除」などの複合条件に向いています。
forfiles で0バイトファイルを削除
@echo off setlocal set TARGET_DIR=C:\work\logs REM @fsize==0 でサイズが0のファイルのみ対象 REM /s でサブフォルダを含める forfiles /p "%TARGET_DIR%" /s /c "cmd /c if @fsize==0 del @path" echo 0バイトファイルの削除完了 endlocal
forfiles: 30日以上前かつ0バイトのファイルを削除
@echo off setlocal set TARGET_DIR=C:\work\logs REM /d -30 は30日以上前のファイルが対象 REM @fsize==0 でさらに0バイトに絞り込む forfiles /p "%TARGET_DIR%" /s /d -30 /c "cmd /c if @fsize==0 del @path" echo 30日以上前の0バイトファイルを削除しました。 endlocal
forfiles の @fsize は未ドキュメントの内部変数
@fsize は forfiles の組み込み変数で、バイト単位のファイルサイズを返します。公式ドキュメントには記載が少ないですが、Windows Vista以降の環境で動作します。@path はファイルの完全パス(ダブルクォート付き)を返します。タスクスケジューラで定期的に自動実行する
0バイトファイルの削除を毎日・毎週自動化するにはWindowsタスクスケジューラに登録します。コマンドラインから登録する方法が最もシンプルです。
schtasks コマンドでタスクスケジューラに登録
@echo off
REM 毎日AM3:00に del_zero.bat を実行するタスクを登録
schtasks /create /tn "ZeroFileCleaner" ^
/tr "C:\scripts\del_zero.bat" ^
/sc daily ^
/st 03:00 ^
/ru SYSTEM ^
/f
echo タスク登録完了
REM タスクの確認
schtasks /query /tn "ZeroFileCleaner"
タスクスケジューラ登録時のポイント
/tn "名前": タスク名(ユニークな名前を付ける)/tr "パス": 実行するバッチファイルの絶対パス/sc daily /st 03:00: 毎日AM3:00に実行/ru SYSTEM: SYSTEMアカウントで実行(ログオフ中でも動作)/f: 既存タスクを上書き
バッチファイル内のパスは必ず絶対パスで記述してください。SYSTEMアカウントはネットワーク共有にアクセスできないため、ネットワーク上のファイルを対象にする場合は専用のサービスアカウントを使用してください。
まとめ
バッチファイルで0バイトファイルを自動削除する方法をまとめます。
- 検出方法:
for %%F in (*) do if %%~zF equ 0が基本 - 再帰処理:
for /rでサブフォルダ含めて処理 - 安全運用: 必ずドライランで対象確認 → 確認プロンプト → 削除
- 拡張子絞り込み:
for /r ... in (*.log)で対象拡張子を限定 - 削除ログ: 日付付きログファイルに削除したファイルを記録
- forfiles: 日付条件との複合削除に便利(
@fsize==0) - 定期実行:
schtasksでタスクスケジューラに登録
関連記事: バッチファイルでファイルを削除する方法完全ガイド / バッチファイルでファイルサイズを取得する完全ガイド / ファイルサイズを条件分岐に活用する完全ガイド
よくある質問(FAQ)
Q削除したファイルをごみ箱に移動したいです(完全削除でなく)。
A
del コマンドはごみ箱を経由せず完全削除します。ごみ箱に移動したい場合は PowerShell -Command "Add-Type -AssemblyName Microsoft.VisualBasic; [Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile('%%F', 'OnlyErrorDialogs', 'SendToRecycleBin')"を使う方法があります。ただしバッチで大量のファイルをごみ箱に移動するとごみ箱管理が煩雑になるため、事前のドライランで対象を十分確認してから完全削除する運用を推奨します。Q0バイトのフォルダ(空フォルダ)も一緒に削除したいです。
A空フォルダの削除は
%%~zF では判定できません(フォルダにはサイズの概念がない)。空フォルダの検出と削除には for /r %%D in (.) do rd "%%D" 2>nul を使います。rd は空でないフォルダには失敗するため、2>nul でエラーを捨てることで空フォルダだけを削除できます。Q読み取り専用属性の0バイトファイルが削除できません。
A読み取り専用(Read-Only)属性が付いたファイルは
del では削除できません。del /f "%%F" の /f オプションで読み取り専用でも強制削除できます。または事前に attrib -r "%%F" で属性を解除してから削除する方法もあります。Qfor /r でループ中に変数が更新されない(%COUNT% が増えない)。
Aバッチの
for ループ内では set /a で設定した変数が即時反映されません。setlocal enabledelayedexpansion を宣言し、%COUNT% の代わりに !COUNT!(感嘆符)で参照してください。遅延展開の詳細はsetlocal enabledelayedexpansion 完全ガイドを参照してください。Q削除してはいけない0バイトファイル(ロックファイル・フラグファイル)があります。
Aアプリが「ファイルの存在で状態を管理する」ために意図的に0バイトファイルを使うことがあります(例:
.lock・.pid・.flag)。これらを誤って削除するとアプリ動作に支障が出ます。対策として①削除対象の拡張子を限定する(*.log・*.tmp のみ)、②除外ファイル名リストを用意して if /i not "%%~nxF"=="app.lock" でスキップする、のどちらかを実装してください。

