forfiles выбирает файлы по маске и дате и выполняет для каждого заданную команду. Самое частое применение - автоматическая очистка: удалить логи, резервные копии или временные файлы старше определённого числа дней. Команда встроена в Windows и Windows Server, ничего устанавливать не нужно.

Синтаксис

batch
forfiles [/P путь] [/M маска] [/S] [/D дата] [/C "команда"]
  • /P - папка, с которой начинать. По умолчанию текущая.
  • /M - маска имени файла, например *.log. По умолчанию * (все файлы).
  • /S - обрабатывать и вложенные папки.
  • /D - отбор по дате изменения:
    • /D -30 - изменённые 30 и более дней назад;
    • /D +30 - формально «с датой изменения не раньше, чем через 30 дней от сегодня», то есть почти ничего не находит. Чтобы выбрать свежие файлы, указывайте дату;
    • /D -дата или /D +дата - до или после конкретной даты. Формат даты зависит от региональных настроек, в русской Windows это ДД.ММ.ГГГГ.
  • /C - команда для каждого файла. По умолчанию "cmd /c echo @file", то есть просто вывод имени.

Переменные внутри команды

Переменная Значение
@file имя файла с расширением, в кавычках
@fname имя без расширения
@ext расширение
@path полный путь к файлу, в кавычках
@relpath путь относительно папки /P
@isdir TRUE для папки, FALSE для файла
@fsize размер в байтах
@fdate, @ftime дата и время изменения

Встроенные команды cmd (del, echo, rmdir, move) нужно вызывать через cmd /c, иначе forfiles их не найдёт.

Примеры

Посмотреть, что будет удалено

Перед удалением всегда проверяйте выборку:

batch
forfiles /P "D:\Logs" /S /M *.log /D -30 /C "cmd /c echo @path @fdate"

Удалить файлы старше 30 дней

batch
forfiles /P "D:\Logs" /S /M *.log /D -30 /C "cmd /c del /q @path"

Удалить только файлы, не трогая папки

Маска * захватывает и папки. Чтобы удалить только файлы:

batch
forfiles /P "D:\Temp" /S /D -7 /C "cmd /c if @isdir==FALSE del /q @path"

Удалить старые папки целиком

Например, папки ежедневных резервных копий старше 14 дней:

batch
forfiles /P "E:\Backup" /D -14 /C "cmd /c if @isdir==TRUE rmdir /s /q @path"

Без /S команда смотрит только на папки первого уровня. Дата изменения папки - это время последнего изменения её списка файлов, а не самого нового файла внутри.

Перенести старые файлы в архивную папку

batch
forfiles /P "D:\Reports" /M *.xlsx /D -90 /C "cmd /c move @path D:\Reports\Archive\"

Найти большие файлы

batch
forfiles /P "D:\Share" /S /C "cmd /c if @fsize GEQ 1073741824 echo @path @fsize"

Сравнение с числами больше 2 ГБ в cmd работает ненадёжно - для больших порогов используйте PowerShell.

Особенности и ошибки

ERROR: No files found with the specified search criteria. Если подходящих файлов нет, forfiles выводит это сообщение и возвращает код ошибки 1. В пакетном файле это может выглядеть как сбой. Сообщение скрывается перенаправлением:

batch
forfiles /P "D:\Logs" /M *.log /D -30 /C "cmd /c del /q @path" 2>nul

Сетевые папки. Путь вида \\server\share в параметре /P не поддерживается. Подключите папку на время работы через pushd - команда сама назначит временную букву диска:

batch
pushd \\fs01\logs
forfiles /S /M *.log /D -30 /C "cmd /c del /q @path"
popd

Пробелы в путях. @path и @file уже в кавычках, дополнительные кавычки вокруг них не нужны. Сам путь в /P берите в кавычки.

Кавычки внутри команды. Если внутри /C нужны свои кавычки, используйте шестнадцатеричный код 0x22: /C "cmd /c echo 0x22@fname0x22".

Запуск по расписанию

Часы со стрелкой и папка с корзиной и отметкой

Сохраните команду в пакетный файл, например C:\Scripts\cleanup.cmd, и создайте задание планировщика:

batch
schtasks /Create /TN "Очистка логов" /TR "C:\Scripts\cleanup.cmd" /SC DAILY /ST 03:00 /RU SYSTEM

Задание будет запускаться каждую ночь от имени системы. Убедитесь, что у учётной записи задания есть права на удаление в нужной папке. Для сетевых папок «Система» не подойдёт: используйте учётную запись с доступом к общей папке.

То же на PowerShell

В PowerShell фильтр по дате пишется прямо в конвейере, и тот же подход работает с сетевыми путями без pushd:

powershell
Get-ChildItem "D:\Logs" -Recurse -File -Filter *.log |
    Where-Object LastWriteTime -lt (Get-Date).AddDays(-30) |
    Remove-Item -WhatIf

Ключ -WhatIf показывает, что будет удалено, не удаляя. Когда результат устраивает, уберите его. Для папок замените -File на -Directory и добавьте -Recurse к Remove-Item.