Узнайте, если файл старше 4 часов в пакетном файле
Проще говоря, как я узнаю, что конкретный файл в папке старше X часов? Имя файла и местоположение всегда будут одинаковыми.
Спасибо
4 ответа
При сравнении времени файла с кодом пакета необходимо учитывать следующее:
- Настройки формата даты и времени
- Файловая система носителей
- Часовой пояс, летнее время
- Автоматическая настройка на летнее время
- Версия Windows
1. Настройки формата даты и времени
Существуют переменные окружения DATE и TIME, которые при доступе (а не при запуске пакетного выполнения) возвращают текущую дату и время в формате, зависящем от настроек Windows Region и Language. Страна определяет формат даты и времени.
Там даже можно определить, например, для немецкого формата короткой даты с DD.MM.YYYY
(день и месяц с нулем в начале) и формат времени с HH:mm:ss
(24-часовой формат с ведущим нулем для часа, минуты и секунды). Но это не значит, что значения DATE и TIME действительно используют этот формат. Например, ВРЕМЯ в формате H:mm:ss,ms
выбрав немецкую страну даже с HH:mm:ss
определено, что означает отсутствие начального нуля в часе, но в минутах и секундах, и дополнительно миллисекунды после запятой в строке времени. Строка даты также может быть с или без дня недели, в зависимости от настроек региона.
И есть образец %~t
чтобы получить дату и время последнего изменения каталога или файла. Запустить в окне командной строки call /?
а также for /?
и прочитайте все справочные страницы, отображаемые для этих двух команд, для получения подробной информации. Формат строки даты и времени, возвращаемой %~t
Как и вывод на вывод команды DIR, зависит также от настроек региона Windows, но обычно не равен формату переменных DATE и TIME. Время последнего изменения файла или каталога обычно возвращается без второго значения в строке даты / времени.
Лучше всего выяснить с помощью следующего пакетного файла, лучше всего выполненного до 10:00, какие форматы на текущем компьютере для текущего пользователя.
@echo off
echo Current date/time: %DATE% %TIME%
for %%I in ("%~f0") do echo Batch file time: %%~tI
pause
2. Файловая система носителя
В файловых системах Windows для хранения файлов используются два формата:
На носителях данных, использующих NTFS, используется точное время NTFS, представляющее собой 64-разрядное число интервалов по 100 наносекунд, прошедшее с 12:00 1 января 1601 года по всемирному координированному времени (UTC).
На носителях данных, использующих FAT, FAT16, FAT32 или exFAT, время файла сохраняется в формате даты-времени DOS с двумя 16-разрядными значениями с разрешением 2 секунды и с использованием текущего местного времени.
Это означает, что нечетная секунда невозможна для файла на носителе FAT, но на носителе NTFS. Копирование файла на диске NTFS с нечетной секундой во время последней модификации на диск FAT32 приводит к наличию идентичных файлов с разницей в 1 секунду во времени последней модификации.
Время файла на носителе NTFS хранится в формате UTC. Так что возвращается %~t
или команда DIR, а также то, что отображается, например, в проводнике Windows, зависит от
- текущий часовой пояс,
- летнее время для этого часового пояса,
- если текущее время находится в пределах летнего времени,
- и если в настоящее время включена автоматическая настройка на летнее время.
Изменение любого из этих 4 параметров приводит к немедленному изменению ВСЕХ отображаемых времен файлов файлов и каталогов на носителе NTFS.
Время файла на носителе FAT является более или менее постоянным, как при использовании локального времени при создании, изменении или последнем доступе, но подробности читайте далее.
3. Часовой пояс, летнее время
Поскольку время файлов на носителе NTFS хранится в формате UTC, но отображается по местному времени, текущий часовой пояс и летнее время для этого часового пояса важны для сравнения времени файлов, особенно когда это делается для файлов на дисках NTFS и FAT32.
Но также для файлов на носителе FAT настройки часового пояса и летнего времени могут быть важны в зависимости от версии Windows. Читайте ниже для деталей.
4. Автоматическая настройка на летнее время
Существует настройка для автоматической настройки перехода на летнее время, которая становится важной, если ее включить, как по умолчанию, и текущий часовой пояс определяет настройку перехода на летнее время.
Например, летнее время в настоящее время активно в Европе. Отключение этой опции в настройках часов Windows приводит к немедленному изменению отображаемого времени файлов на носителе NTFS на -1 час, а также большого количества файлов на носителе FAT в зависимости от версии Windows.
Эта разница в 1 час, вызванная переходом на летнее время, должна учитываться при сравнении времени файлов на носителях NTFS и FAT.
5. Версия Windows
NTFS поддерживается всеми версиями Windows NT. Поведение файлового времени одинаково для файлов и каталогов во всех версиях Windows, поддерживающих NTFS.
Но то, как время файла на носителе FAT интерпретируется, зависит от версии Windows. В предыдущих версиях Windows Vista время файлов на носителе FAT не зависело от изменения часового пояса, установки летнего времени и автоматической настройки летнего времени. Дата и время последнего изменения файла постоянны в Windows 2000 и Windows XP.
Но в Windows Vista и более поздних версиях время отображения файлов на носителе FAT зависит от летнего времени и автоматической настройки на летнее время. Часовой пояс напрямую не имеет значения. Выбор другого часового пояса, который используется в настоящее время, не меняет отображаемое время файлов на носителе FAT, как это происходит для файлов на носителе NTFS. Но если текущее время находится в пределах периода летнего времени активного часового пояса, и включена автоматическая настройка летнего времени, а локальное время файла или каталога находится в пределах периода летнего времени, Windows Vista добавляет +1 час. и позже. Файлы, последние измененные на дисках FAT в течение стандартного времени, являются постоянными в течение года; только файлы, последние измененные в течение периода летнего времени, отображаются с +1 часом или без него в зависимости от текущего времени с включенной автоматической настройкой летнего времени.
Такое разное управление временем файлов FAT может быть крайне запутанным, когда, например, имеется общий общий доступ к папке на диске FAT32 на компьютере под управлением Windows 7 и если он подключен к этой общей папке на компьютере под управлением Windows XP. Файл, последний раз измененный 2015-09-19 17:39:08, хранящийся на диске FAT32 на ПК с Windows 7, сегодня отображается Windows 7 с часовым поясом Германии с временем файла 19.09.2015 17:39:08, но через 2 месяца, хотя не изменяется с 19.09.2015 16:39:08, и Windows XP отображает тот же файл на подключенном ПК с Windows 7 сегодня и в будущем с постоянным временем файла 19.09.2015 17:39:08.
Сравнение времени файлов, хранящихся в архивах (ZIP, RAR, ...), с файлами на носителях NTFS или FAT может быть настоящим кошмаром.
6. Сравнение времени файла с текущим временем
Для этой задачи, сравнивая время последнего изменения файла с текущим временем, должно быть достаточно просто учесть настройки формата даты и времени.
Приведенный ниже пакетный код использует код, подробно описанный в моем ответе на пакетный файл, для удаления файлов старше N дней. Это объяснение следует прочитать перед использованием кода ниже.
В зависимости от формата даты / времени переменных DATE и TIME и строки даты / времени, возвращаемой %~tI
для файла на локальном ПК с Windows может потребоваться внести небольшие изменения в код. Соответствующие подсказки даны в строках комментария кода партии.
@echo off
setlocal EnableExtensions
rem Get seconds since 1970-01-01 for current date and time.
rem From date string only the last 10 characters are passed to GetSeconds
rem which results in passing dd/mm/yyyy or dd.mm.yyyy in expected format
rem to this subroutine independent on date string of environment variable
rem DATE is with or without abbreviated weekday at beginning.
call :GetSeconds "%DATE:~-10% %TIME%"
rem Subtract seconds for 4 hours (4 * 3600 seconds) from seconds value.
set /A "CompareTime=Seconds-4*3600"
rem Define batch file itself as the file to compare time by default.
set "FileToCompareTime=%~f0"
rem If batch file is started with a parameter and the parameter
rem specifies an existing file (or directory), compare with last
rem modification date of this file.
if not "%~1" == "" (
if exist "%~1" set "FileToCompareTime=%~1"
)
rem Get seconds value for the specified file.
for %%F in ("%FileToCompareTime%") do call :GetSeconds "%%~tF:0"
rem Compare the two seconds values.
if %Seconds% LSS %CompareTime% (
echo File %FileToCompareTime% was last modified for more than 4 hours.
) else (
echo File %FileToCompareTime% was last modified within the last 4 hours.
)
endlocal
goto :EOF
rem No validation is made for best performance. So make sure that date
rem and hour in string is in a format supported by the code below like
rem MM/DD/YYYY hh:mm:ss or M/D/YYYY h:m:s for English US date/time.
:GetSeconds
rem If there is " AM" or " PM" in time string because of using 12 hour
rem time format, remove those 2 strings and in case of " PM" remember
rem that 12 hours must be added to the hour depending on hour value.
set "DateTime=%~1"
set "Add12Hours=0"
if "%DateTime: AM=%" NEQ "%DateTime%" (
set "DateTime=%DateTime: AM=%"
) else if "%DateTime: PM=%" NEQ "%DateTime%" (
set "DateTime=%DateTime: PM=%"
set "Add12Hours=1"
)
rem Get year, month, day, hour, minute and second from first parameter.
for /F "tokens=1-6 delims=,-./: " %%A in ("%DateTime%") do (
rem For English US date MM/DD/YYYY or M/D/YYYY
set "Day=%%B" & set "Month=%%A" & set "Year=%%C"
rem For German date DD.MM.YYYY or English UK date DD/MM/YYYY
rem set "Day=%%A" & set "Month=%%B" & set "Year=%%C"
set "Hour=%%D" & set "Minute=%%E" & set "Second=%%F"
)
rem Remove leading zeros from the date/time values or calculation could be wrong.
if "%Month:~0,1%" EQU "0" ( if "%Month:~1%" NEQ "" set "Month=%Month:~1%" )
if "%Day:~0,1%" EQU "0" ( if "%Day:~1%" NEQ "" set "Day=%Day:~1%" )
if "%Hour:~0,1%" EQU "0" ( if "%Hour:~1%" NEQ "" set "Hour=%Hour:~1%" )
if "%Minute:~0,1%" EQU "0" ( if "%Minute:~1%" NEQ "" set "Minute=%Minute:~1%" )
if "%Second:~0,1%" EQU "0" ( if "%Second:~1%" NEQ "" set "Second=%Second:~1%" )
rem Add 12 hours for time range 01:00:00 PM to 11:59:59 PM,
rem but keep the hour as is for 12:00:00 PM to 12:59:59 PM.
if "%Add12Hours%" == "1" (
if %Hour% LSS 12 set /A Hour+=12
)
set "DateTime="
set "Add12Hours="
rem Must use 2 arrays as more than 31 tokens are not supported
rem by command line interpreter cmd.exe respectively command FOR.
set /A "Index1=Year-1979"
set /A "Index2=Index1-30"
if %Index1% LEQ 30 (
rem Get number of days to year for the years 1980 to 2009.
for /F "tokens=%Index1% delims= " %%Y in ("3652 4018 4383 4748 5113 5479 5844 6209 6574 6940 7305 7670 8035 8401 8766 9131 9496 9862 10227 10592 10957 11323 11688 12053 12418 12784 13149 13514 13879 14245") do set "Days=%%Y"
for /F "tokens=%Index1% delims= " %%L in ("Y N N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N") do set "LeapYear=%%L"
) else (
rem Get number of days to year for the years 2010 to 2038.
for /F "tokens=%Index2% delims= " %%Y in ("14610 14975 15340 15706 16071 16436 16801 17167 17532 17897 18262 18628 18993 19358 19723 20089 20454 20819 21184 21550 21915 22280 22645 23011 23376 23741 24106 24472 24837") do set "Days=%%Y"
for /F "tokens=%Index2% delims= " %%L in ("N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N N") do set "LeapYear=%%L"
)
rem Add the days to month in year.
if "%LeapYear%" == "N" (
for /F "tokens=%Month% delims= " %%M in ("0 31 59 90 120 151 181 212 243 273 304 334") do set /A "Days+=%%M"
) else (
for /F "tokens=%Month% delims= " %%M in ("0 31 60 91 121 152 182 213 244 274 305 335") do set /A "Days+=%%M"
)
rem Add the complete days in month of year.
set /A "Days+=Day-1"
rem Calculate the seconds which is easy now.
set /A "Seconds=Days*86400+Hour*3600+Minute*60+Second"
rem Exit this subroutine
goto :EOF
rojo добавил информацию о том, как можно игнорировать настройки формата даты и времени, файловую систему и версию Windows с помощью wmic - утилиты командной строки Windows Management Instrumentation.
Использование wmic имеет преимущество фиксированного формата для строк даты / времени, и, следовательно, пакетный код работает на всех компьютерах Windows без адаптации к локальному формату даты / времени.
Также версия Windows не имеет значения при использовании wmic из-за внутреннего использования функции GetFileTime (скорее всего).
Файловая система становится важной только при оценке минутного смещения местного времени / времени файла в UTC. Команда wmic возвращает только на FAT диски +***
для смещения минут в UTC, в то время как смещение минут является правильным для времени последнего изменения файла на диске NTFS.
Пример для одного и того же файла на диске NTFS и FAT32:
NTFS: 20071011192622.000000+120
FAT32: 20071011192622.000000+***
+120 - это сумма +60 минут для CET (центральноевропейское время) и +60 минут для активного летнего времени или, другими словами, +120 минут для CEST (центральноевропейское летнее время).
Поэтому приведенный ниже пакетный код не оценивает смещение минут в UTC, которое не требуется при сравнении времени последнего изменения файла с текущим местным временем.
Кроме того, имя файла должно быть указано всегда с полным путем в командной строке wmic. Просто указать имя файла для файла в текущем каталоге или имя файла с относительным путем не работает. И каждая обратная косая черта должна быть экранирована еще одной обратной косой чертой в имени файла с путем.
Основным недостатком использования wmic является скорость. Код пакета выше на моем компьютере должен завершиться в течение 50 мс согласно журналу Process Monitor (в среднем за несколько запусков). Приведенный ниже пакетный код, вызывающий wmic дважды, требует для выполнения одной и той же задачи около 1500 мс (также среднее значение). Поэтому использование wmic для большого количества файлов определенно не очень хорошая идея, если этого можно избежать, поскольку известен локальный формат даты / времени.
@echo off
setlocal EnableExtensions
rem Get seconds since 1970-01-01 for current date and time.
for /F "tokens=2 delims==." %%T in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do call :GetSeconds %%T
rem Subtract seconds for 4 hours (4 * 3600 seconds) from seconds value.
set /A "CompareTime=Seconds-4*3600"
rem Define batch file itself as the file to compare time by default.
set "FileToCompareTime=%~f0"
rem If batch file is started with a parameter and the parameter
rem specifies an existing file (or directory), compare with last
rem modification date of this file.
if not "%~1" == "" (
if exist "%~f1" set "FileToCompareTime=%~f1"
)
rem Get seconds value for the specified file.
set "DataFile=%FileToCompareTime:\=\\%"
for /F "usebackq tokens=2 delims==." %%T in (`%SystemRoot%\System32\wbem\wmic.exe DATAFILE where "name='%DataFile%'" GET LastModified /VALUE`) do echo call :GetSeconds %%T
rem Compare the two seconds values.
if %Seconds% LSS %CompareTime% (
echo File %FileToCompareTime% was last modified for more than 4 hours.
) else (
echo File %FileToCompareTime% was last modified within the last 4 hours.
)
endlocal
goto :EOF
rem Date/time format used by the command line utility of Windows
rem Management Instrumentation is always YYYYMMDDHHmmss with a
rem dot and a 6 digit microsecond value and plus/minus 3 digit
rem time offset to UTC in minutes independent on region and
rem language settings. Microsecond is always 000000 for a file
rem time. The minutes offset including time zone offset and
rem current daylight saving time offset is returned for a file
rem time only for a file on an NTFS drive. Minutes offset is
rem +*** for a file on a FAT drive (at least on Windows XP).
:GetSeconds
rem Get year, month, day, hour, minute and second from first parameter.
set "DateTime=%~1"
set "Year=%DateTime:~0,4%"
set "Month=%DateTime:~4,2%"
set "Day=%DateTime:~6,2%"
set "Hour=%DateTime:~8,2%"
set "Minute=%DateTime:~10,2%"
set "Second=%DateTime:~12,2%"
rem echo Date/time is: %Year%-%Month%-%Day% %Hour%:%Minute%:%Second%
rem Remove leading zeros from the date/time values or calculation could be wrong.
if "%Month:~0,1%" EQU "0" ( if "%Month:~1%" NEQ "" set "Month=%Month:~1%" )
if "%Day:~0,1%" EQU "0" ( if "%Day:~1%" NEQ "" set "Day=%Day:~1%" )
if "%Hour:~0,1%" EQU "0" ( if "%Hour:~1%" NEQ "" set "Hour=%Hour:~1%" )
if "%Minute:~0,1%" EQU "0" ( if "%Minute:~1%" NEQ "" set "Minute=%Minute:~1%" )
if "%Second:~0,1%" EQU "0" ( if "%Second:~1%" NEQ "" set "Second=%Second:~1%" )
rem Must use 2 arrays as more than 31 tokens are not supported
rem by command line interpreter cmd.exe respectively command FOR.
set /A "Index1=Year-1979"
set /A "Index2=Index1-30"
if %Index1% LEQ 30 (
rem Get number of days to year for the years 1980 to 2009.
for /F "tokens=%Index1% delims= " %%Y in ("3652 4018 4383 4748 5113 5479 5844 6209 6574 6940 7305 7670 8035 8401 8766 9131 9496 9862 10227 10592 10957 11323 11688 12053 12418 12784 13149 13514 13879 14245") do set "Days=%%Y"
for /F "tokens=%Index1% delims= " %%L in ("Y N N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N") do set "LeapYear=%%L"
) else (
rem Get number of days to year for the years 2010 to 2038.
for /F "tokens=%Index2% delims= " %%Y in ("14610 14975 15340 15706 16071 16436 16801 17167 17532 17897 18262 18628 18993 19358 19723 20089 20454 20819 21184 21550 21915 22280 22645 23011 23376 23741 24106 24472 24837") do set "Days=%%Y"
for /F "tokens=%Index2% delims= " %%L in ("N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N N") do set "LeapYear=%%L"
)
rem Add the days to month in year.
if "%LeapYear%" == "N" (
for /F "tokens=%Month% delims= " %%M in ("0 31 59 90 120 151 181 212 243 273 304 334") do set /A "Days+=%%M"
) else (
for /F "tokens=%Month% delims= " %%M in ("0 31 60 91 121 152 182 213 244 274 305 335") do set /A "Days+=%%M"
)
rem Add the complete days in month of year.
set /A "Days+=Day-1"
rem Calculate the seconds which is easy now.
set /A "Seconds=Days*86400+Hour*3600+Minute*60+Second"
rem Exit this subroutine
goto :EOF
В качестве альтернативы вы можете создать простую программу, используя C#, например, так:
// compile using c:\Windows\Microsoft.NET\Framework\v3.5\csc FileByAge.cs
using System;
using System.IO;
public static class Program
{
public static void Main(string[] args)
{
double age = double.Parse(args[0]);
string fileName;
while ((fileName = Console.ReadLine()) != null)
{
if(age >= (DateTime.Now - new FileInfo(fileName).CreationTime).TotalHours)
{
continue;
}
Console.WriteLine(fileName);
}
}
}
Затем эту программу можно использовать для поиска всех файлов старше 2 часов в пакетном файле, например:
for /F "delims=" %f in ('dir /B *.txt^|FileByAge.exe 2') do echo %f
Улучшенная и самостоятельно компилируемая версия
Как упомянул @Paul, можно даже создать хакерский гибридный пакетный файл / C#, который будет компилироваться и запускаться сам:
@echo off
setlocal
set CsFile=%TEMP%\%~n0.cs
set ExeFile=%TEMP%\%~n0.exe
pushd %SystemRoot%\Microsoft.NET\Framework
for /F %%f in ('dir /B /O:-N v*') do cd %%f & goto :BreakLoop;
:BreakLoop
(echo /* & type "%~f0") > "%CsFile%"
csc.exe /nologo /out:"%ExeFile%" "%CsFile%"
del "%CsFile%"
popd
more | "%ExeFile%" %*
del "%ExeFile%"
endlocal
goto:eof
*/
using System;
using System.IO;
public static class Program
{
public static void Main(string[] args)
{
var olderThen = args[0].Contains("older");
var targetAge = double.Parse(args[1]);
string fileName;
while ((fileName = Console.ReadLine()) != null)
{
if (string.Empty == fileName)
{
continue;
}
var fileAge = (DateTime.Now - new FileInfo(fileName).CreationTime).TotalHours;
if (olderThen ? targetAge > fileAge : targetAge < fileAge)
{
continue;
}
Console.WriteLine(fileName);
}
}
}
И может быть использовано так:
for /F "delims=" %f in ('dir /B *.txt^|FileByAge.bat older 2') do echo %f
Решение этого будет во многом зависеть от ожидаемого временного диапазона, так как AFAIK вы не получите время как одно целое число в пакетном файле без внешних программ. Это означает, что вам нужно проанализировать значение времени в вашем скрипте, а объем анализа и количество необходимых вычислений зависят от диапазона, для которого он требуется. Вот одно простое решение. Предполагается, что файл не может быть старше 24 часов.
set "filename=myfile.txt"
rem extract current date and time
for /f "tokens=1-5 delims=.:, " %%a in ("%date% %time%") do (
set day=%%a&set mon=%%b&set yr=%%c&set hr=%%d&set min=%%e
)
rem extract file date and time
for /f "tokens=1-5 delims=.:, " %%a in ('"dir %filename%|find "%filename%""') do (
set fday=%%a&set fmon=%%b&set fyr=%%c&set fhr=%%d&set fmin=%%e
)
rem calculate age of file (in minutes)
set /a "age=((hr*60+min)-(fhr*60+fmin)+(24*60))%%(24*60)"
set /a "max=4*60"
if %age% geq %max% echo.file is older than 4 hours
Чистая партия ужасна на сегодняшний день по математике. Что произойдет, если ваш сценарий будет запущен вскоре после полуночи? Что если проверка времени охватывает переход на летнее время?
Я предлагаю вызвать Windows Script Host и заимствовать язык, который лучше обрабатывает математику даты / времени. Вот гибридный пакетный / JScript-скрипт, который довольно легко вычислит возраст файла или каталога. Сохраните его с расширением.bat и сделайте снимок.
@if (@CodeSection==@Batch) @then
@echo off
setlocal
set "file=C:\Path\To\File.ext"
for /f %%I in ('cscript /nologo /e:JScript "%~f0" "%file%"') do (
rem // value contained in %%I is in minutes.
if %%I gtr 240 (
echo %file% is over 4 hours old.
rem // do stuff here to take whatever actions you wish
rem // if the file is over 4 hours old.
)
)
goto :EOF
@end // end batch portion / begin JScript hybrid chimera
var fso = new ActiveXObject("scripting.filesystemobject"),
arg = WSH.Arguments(0),
file = fso.FileExists(arg) ? fso.GetFile(arg) : fso.GetFolder(arg);
// Use either DateCreated, DateLastAccessed, or DateLastModified.
// See http://msdn.microsoft.com/en-us/library/1ft05taf%28v=vs.84%29.aspx
// for more info.
var age = new Date() - file.DateLastModified;
WSH.Echo(Math.floor(age / 1000 / 60));
Для получения дополнительной информации о пакетных / JScript гибридных сценариях см. Эту страницу GitHub. Стиль, использованный выше, похож на Hybrid.bat на этой странице. Этот ответ основан на другом. Что бы это ни стоило, PowerShell также хорош в обработке математики дат; но WSH выполняется намного быстрее.