Узнайте, если файл старше 4 часов в пакетном файле - PullRequest
7 голосов
/ 03 августа 2011

Проще говоря, как бы я узнал, если конкретный файл в папке старше X часов?Имя и местоположение файла всегда будут одинаковыми.

Спасибо

Ответы [ 4 ]

11 голосов
/ 19 сентября 2015

При сравнении времени файла с кодом пакета необходимо учитывать следующее:

  1. Настройки формата даты и времени
  2. Файловая система носителя
  3. Часовой пояс, летнее время
  4. Автоматическая настройка на летнее время
  5. Версия Windows

1. Настройки формата даты и времени

Существуют переменные среды ДАТА и ВРЕМЯ , которые при доступе (а не при запуске пакетного выполнения) возвращают текущую дату и время в формате, зависящем от Регион Windows и языковые настройки . Страна определяет формат даты и времени.

Здесь даже можно определить, например, для немецкого формата короткой даты с DD.MM.YYYY (день и месяц с начальным нулем) и формата времени с HH:mm:ss (24-часовой формат с начальным нулем для часа, минуты и секунды) , Но это не означает, что значения DATE и TIME действительно используют этот формат. Например, TIME имеет формат H:mm:ss,ms, если выбрана немецкая страна, даже если определено HH:mm:ss, что означает отсутствие начального нуля в часах, но в минутах и ​​секундах, и дополнительно в миллисекундах после запятой в строке времени , Строка даты также может быть с или без дня недели в зависимости от настроек региона.

И есть шаблон %~t для получения даты и времени последнего изменения каталога или файла. Запустите в окне командной строки call /? и for /? и прочитайте все страницы справки, отображаемые для этих двух команд, для получения подробной информации. Формат строки даты и времени, возвращаемой %~t, а также отображаемой при выводе команды DIR , зависит также от настроек региона Windows, но обычно не равен формату переменных DATE и ВРЕМЯ . Время последнего изменения файла или каталога обычно возвращается без второго значения в строке даты / времени.

Лучше всего выяснить с помощью следующего пакетного файла, лучше всего выполненного до 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 Date Time с двумя 16-битными значениями с разрешением 2 секунды и с использованием текущего местного времени. .

Это означает, что нечетная секунда невозможна для файла на носителе FAT, но на носителе NTFS. Копирование файла на диске NTFS с нечетной секундой во время последней модификации на диск FAT32 приводит к тому, что идентичные файлы имеют разницу в 1 секунду во время последней модификации.

Время файлов на носителе NTFS хранится в формате UTC. Так что то, что возвращается %~t или командой DIR , а также то, что отображается, например, в Windows Explorer, зависит от

  • текущий часовой пояс,
  • летнее время для этого часового пояса,
  • , если текущее время находится в пределах летнего времени,
  • и если в настоящее время включена автоматическая настройка на летнее время.

Изменение любого из этих 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 дней .Это объяснение следует прочитать перед использованием приведенного ниже кода.

В зависимости от формата даты / времени переменных ДАТА и ВРЕМЯ и строки даты / времени, возвращаемой %~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.

Использование 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
3 голосов
/ 30 марта 2012

В качестве альтернативы вы можете создать простую программу, используя 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
2 голосов
/ 19 сентября 2015

Чистая партия ужасна на сегодняшний день по математике. Что произойдет, если ваш сценарий будет запущен вскоре после полуночи? Что если проверка времени охватывает переход на летнее время?

Я предлагаю вызвать 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 выполняется намного быстрее.

2 голосов
/ 08 августа 2011

Решение этого будет во многом зависеть от ожидаемого временного диапазона, так как 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
...