Descubra se o arquivo tem mais de 4 horas em arquivo em lote

Muito simplesmente, como eu descobriria se um arquivo específico em uma pasta é mais antigo do que X horas? O nome e a localização do arquivo serão sempre os mesmos.

obrigado

Ao comparar os tempos de arquivo com o código do lote, deve-se levar em consideração:

  1. Configurações de formato de data e hora
  2. Sistema de arquivos de mídia de armazenamento
  3. Fuso horário, horário de verão
  4. Ajuste automático para horário de verão
  5. Versão do Windows

1. Configurações de formato de data e hora

Existem as variables ​​de ambiente DATE e TIME que, no access (e não no início da execução em lote), retornam a data e a hora atuais em um formato, dependendo das configurações de região e idioma do Windows . O país define o formato de data e hora.

É até mesmo possível definir, por exemplo, o formato alemão de data abreviada com DD.MM.YYYY (dia e mês com zero à esquerda) e formato de hora com HH:mm:ss (formato de 24 horas com zero à esquerda para hora, minuto e segundo ). Mas isso não significa que o valor de DATE e TIME estejam realmente usando esse formato. Por exemplo, TIME está no formato H:mm:ss,ms ao ter selecionado um país alemão, mesmo com HH:mm:ss definido, o que significa zero zero à hora, mas no minuto e segundo, além disso, milissegundos depois de uma vírgula no tempo . A string de data pode ser também com ou sem dia da semana, dependendo das configurações da região.

E existe o padrão %~t para obter a data e hora da última modificação de um diretório ou arquivo. Executar em uma call /? janela de prompt de comando call /? e for /? e leia todas as páginas de ajuda exibidas para esses dois comandos para detalhes. O formato da string de data e hora retornada por %~t , bem como exibido na saída do comando DIR, também depende das configurações da região do Windows, mas geralmente não é igual ao formato das variables DATE e TIME . A hora da hora da última modificação de um arquivo ou diretório geralmente é retornada sem o segundo valor na cadeia de data / hora.

Melhor é descobrir com o seguinte arquivo de lote melhor executado antes de 10:00, quais são os formatos na máquina atual para o usuário atual.

 @echo off echo Current date/time: %DATE% %TIME% for %%I in ("%~f0") do echo Batch file time: %%~tI pause 

2. Sistema de arquivos da mídia de armazenamento

Nos filesystems do Windows, dois formatos de armazenamento são usados ​​para os tempos de arquivo :

  • Na mídia de armazenamento que usa o NTFS, o tempo de precisão do NTFS é usado, que é um número de 64 bits de intervalos de 100 nanossegundos transcorridos desde 12 de janeiro de 1601, hora universal coordenada (UTC).

  • Em mídia de armazenamento usando FAT, FAT16, FAT32 ou exFAT, os tempos de arquivo são armazenados usando o formato Data / Hora do DOS com dois valores de 16 bits com uma resolução de 2 segundos e usando a hora local atual.

Isso significa que um segundo ímpar não é possível para um arquivo na mídia FAT, mas na mídia NTFS. Copiar um arquivo em uma unidade NTFS com um segundo ímpar na hora da última modificação em uma unidade FAT32 resulta em arquivos idênticos com uma diferença de 1 segundo na hora da última modificação.

Os tempos de arquivo na mídia NTFS são armazenados em UTC. Então, o que é retornado por %~t ou comando DIR , bem como o que é exibido, por exemplo, no Windows Explorer depende

  • fuso horário atual,
  • horário de verão para esse fuso horário,
  • se a hora atual estiver dentro do período de horário de verão,
  • e se o ajuste automático para o horário de verão estiver ativado no momento.

A alteração de qualquer um desses 4 parâmetros resulta em uma alteração imediata de TODOS os horários de arquivo exibidos dos arquivos e diretórios na mídia de armazenamento NTFS.

Os tempos de arquivo na mídia FAT são mais ou menos constantes como o uso da hora local na criação, modificação ou último access, mas leia mais para detalhes.


3. fuso horário, horário de verão

Como os tempos de arquivo na mídia NTFS são armazenados em UTC, mas exibidos na hora local, o fuso horário atual e o horário de verão para esse fuso horário são importantes para comparações de tempo de arquivo, especialmente quando feitos para arquivos em unidades NTFS e FAT32.

Mas também para arquivos em uma mídia FAT, as configurações de fuso horário e horário de verão podem ser importantes dependendo da versão do Windows. Leia abaixo para detalhes.


4. Ajuste automático para horário de verão

Há a configuração para o ajuste automático do horário de verão, que se torna importante quando é habilitado como padrão e o fuso horário definido atualmente define um ajuste do horário de verão.

Por exemplo, o horário de verão está ativo atualmente na Europa. Desativar essa opção nas configurações do relógio do Windows resulta em uma alteração imediata dos tempos de arquivos exibidos na mídia NTFS em -1 hora e também em muitos arquivos na mídia FAT, dependendo da versão do Windows.

Essa diferença de hora de 1 hora causada pelo ajuste do horário de verão deve ser levada em consideração na comparação de tempos de arquivo em mídia NTFS e FAT.


5. Versão do Windows

O NTFS é suportado por todas as versões do Windows NT. O comportamento de tempos de arquivo é igual para arquivos e diretórios em todas as versões do Windows que suportam NTFS.

Mas como os tempos de arquivo na mídia FAT são interpretados depende da versão do Windows. Antes do Windows Vista, os tempos de arquivo na mídia FAT independem da alteração do fuso horário, da configuração do horário de verão e do ajuste automático do horário de verão. A data / hora da última modificação de um arquivo é constante no Windows 2000 e no Windows XP.

Mas no Windows Vista e mais tarde, os tempos de arquivo exibidos na mídia FAT dependem do horário de verão e do ajuste automático do horário de verão. O fuso horário não importa diretamente. A seleção de outro fuso horário como usado atualmente não altera os tempos de arquivos exibidos em arquivos em mídia FAT, como ocorre com arquivos em mídia NTFS. Mas se a hora atual estiver dentro do período de horário de verão do fuso horário ativo e o ajuste automático do horário de verão estiver ativado e a hora local do arquivo ou diretório estiver dentro do período de horário de verão, a hora +1 será adicionada pelo Windows Vista e depois. Os arquivos modificados pela última vez em unidades FAT dentro do horário padrão são constantes ao longo do ano; apenas os arquivos modificados pela última vez no período de DST são exibidos com ou sem +1 hora, dependendo da hora atual com o ajuste automático de horário de verão ativado.

Esse gerenciamento de tempo de arquivo FAT diferente pode ser extremamente confuso ao ter, por exemplo, um compartilhamento público de uma pasta em uma unidade FAT32 em uma máquina com Windows 7 e estar conectado a essa pasta pública com uma máquina com Windows XP. Um arquivo modificado pela última vez em 2015-09-19 17:39:08 armazenado na unidade FAT32 no Windows 7 PC é exibido hoje pelo Windows 7 com o fuso horário alemão com o tempo de arquivo 19.09.2015 17:39:08, mas em 2 meses embora não modificado com 19.09.2015 16:39:08, e Windows XP exibe o mesmo arquivo no PC Windows 7 conectado hoje e no futuro constante com o tempo de arquivo 19.09.2015 17:39:08.

Comparar tempos de arquivo de arquivos armazenados em arquivos (ZIP, RAR, …) com arquivos em mídia NTFS ou FAT pode ser realmente um pesadelo.


6. Comparando o tempo do arquivo com o horário atual

Para esta tarefa, comparando a hora da última modificação de um arquivo com a hora atual, deve ser suficiente apenas considerar as configurações de formato de data e hora.

O código do lote abaixo usa o código explicado em detalhes na minha resposta no arquivo em lote para excluir arquivos com mais de N dias . Esta explicação deve ser lida antes de usar o código abaixo.

Dependendo do formato de data / hora das variables DATE e TIME e da sequência de data / hora retornada por %~tI para o arquivo no Windows PC local, pode ser necessário fazer pequenas modificações no código. Sugestões apropriadas são dadas nas linhas de comentário do código de lote.

 @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 ("YNNNYNNNYNNNYNNNYNNNY NNNYNNNYN") 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 ("NNYNNNYNNNYNNNYNNNYNN NYNNNYNN") 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 

O rojo incluiu informações sobre como as configurações de formato de data e hora, sistema de arquivos e versão do Windows podem ser ignoradas usando o wmic – o utilitário de linha de comando do Windows Management Instrumentation.

O uso do wmic tem a vantagem do formato fixo para as strings de data / hora e, portanto, o código do lote funciona em todos os computadores Windows sem adaptações ao formato de data / hora local.

Também a versão do Windows não importa em usar o wmic por causa do uso interno da function GetFileTime (muito provavelmente).

O sistema de arquivos torna-se importante apenas ao avaliar também o deslocamento de minutos do horário local / tempo do arquivo para UTC. Comando wmic retorna em unidades FAT apenas +*** para o deslocamento de minutos para UTC enquanto o deslocamento de minutos está correto para o tempo de arquivo da última modificação de um arquivo em uma unidade NTFS.

Exemplo para o mesmo arquivo na unidade NTFS e FAT32:

 NTFS: 20071011192622.000000+120 FAT32: 20071011192622.000000+*** 

+120 é a sum de +60 minutos para CET (Hora da Europa Central) e +60 minutos para o horário de verão ativo, ou em outras palavras, +120 minutos para o CEST (horário de verão da Europa Central).

Portanto, o código de lote abaixo não avalia o deslocamento de minutos para UTC, que não é necessário ao comparar o tempo de arquivo da última modificação de um arquivo com a hora local atual.

Além disso, o nome do arquivo deve ser especificado sempre com caminho completo na linha de comando wmic . Apenas especificar o nome do arquivo para um arquivo no diretório atual ou um nome de arquivo com caminho relativo não funciona. E cada barra invertida deve ser escapada com mais uma barra invertida no nome do arquivo com o caminho.

A principal desvantagem do uso do wmic é a velocidade. O código de lote acima requer no meu computador cerca de 50 ms para terminar de acordo com o log do Process Monitor (média de várias execuções). O código de lote abaixo chamado wmic precisa duas vezes para a mesma tarefa, cerca de 1500 ms para terminar (também um valor médio). Portanto, usar o wmic em um grande número de arquivos definitivamente não é uma boa idéia se puder ser evitado porque o formato de data / hora local é conhecido.

 @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 ("YNNNYNNNYNNNYNNNYNNNY NNNYNNNYN") 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 ("NNYNNNYNNNYNNNYNNNYNN NYNNNYNN") 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 

Alternativamente, você poderia criar um programa simples usando C #, por exemplo, assim:

 // 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); } } } 

Esse programa poderia então ser usado para encontrar todos os arquivos com mais de 2 horas em um arquivo de lote como este:

 for /F "delims=" %f in ('dir /B *.txt^|FileByAge.exe 2') do echo %f 

Uma versão melhorada e auto-compilável

Como mencionado pelo @Paul, pode-se até construir um arquivo batch / c # híbrido hacky que será compilado e executado:

 @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); } } } 

E pode ser usado assim:

 for /F "delims=" %f in ('dir /B *.txt^|FileByAge.bat older 2') do echo %f 

A solução para isso dependerá muito do intervalo de tempo esperado, já que o AFAIK não recebe tempo como um inteiro em um arquivo em lote sem programas externos. Isso significa que você precisa analisar o valor de tempo em seu script e a quantidade de análise e o número de cálculos necessários, dependendo do intervalo necessário para o trabalho. Aqui está uma solução simples. Assume-se que o arquivo não pode ser mais antigo que 24 horas.

 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 

Lote puro é horrível na data matemática. O que acontece se o seu script for executado pouco depois da meia-noite? E se a verificação do tempo abranger uma alteração do horário de verão?

Eu proponho invocar o Windows Script Host e tomar emprestado de uma linguagem que lida melhor com a matemática de data / hora. Aqui está um script híbrido em lote / JScript que calcula a idade de um arquivo ou diretório facilmente. Salve-o com uma extensão .bat e dê uma chance.

 @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)); 

Para obter mais informações sobre scripts híbridos em lote / JScript, consulte esta página do GitHub . O estilo usado acima é semelhante ao Hybrid.bat nessa página. Essa resposta é baseada em outra . Por que vale a pena, PowerShell também é bom em lidar com matemática de data; mas o WSH é executado muito mais rapidamente.