在批处理文件中查明文件是否超过4小时

时间:2011-08-03 14:58:43

标签: batch-file command-line batch-processing

很简单,我怎样才能知道文件夹中的特定文件是否超过X小时? 文件名和位置将始终相同。

由于

4 个答案:

答案 0 :(得分:11)

在比较文件时间与批次代码时,必须考虑以下事项:

  1. 日期和时间格式设置
  2. 存储媒体的文件系统
  3. 时区,夏令时
  4. 自动调整夏令时
  5. Windows版本
  6. 1。日期和时间格式设置

    环境变量 DATE TIME 访问(而不是批处理执行开始时)返回当前< / strong>日期和时间,格式取决于Windows Region and Language settings。国家/地区定义日期和时间格式。

    甚至可以定义例如德语短日期格式DD.MM.YYYY(日期和月份,前导零)和时间格式HH:mm:ss(24小时格式,前导零小时,分钟和秒钟)。但这并不意味着 DATE TIME 的值实际上正在使用此格式。例如, TIME 格式为H:mm:ss,ms时,即使定义了HH:mm:ss也选择了德国国家/地区,这意味着小时,但是分钟和秒,以及之后的毫秒时间字符串中的逗号。根据地区设置,日期字符串也可以有或没有工作日。

    还有模式%~t来获取目录或文件的最后修改日期和时间。在命令提示符窗口call /?for /?中运行,并阅读为这两个命令显示的所有帮助页面以获取详细信息。 %~t返回的日期和时间字符串格式以及 DIR 输出中显示的格式也取决于Windows区域设置,但通常不等于变量格式日期 TIME 。通常在日期/时间字符串中返回没有第二个值的文件或目录的上次修改时间。

    最好是在上午10:00之前找出最佳执行的以下批处理文件。当前用户当前用户的格式是什么。

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

    2。存储介质的文件系统

    在Windows文件系统上,file times使用了两种存储格式:

    • 在使用 NTFS 的存储媒体上使用NTFS precision time,这是自上午12点以来已经过的64位数100纳秒间隔。 1601年1月1日世界协调时间(UTC)。

    • 在使用FAT,FAT16,FAT32或exFAT的存储介质上,文件时间使用DOS Date Time格式存储,两个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版本而异。

    在比较NTFS和FAT媒体上的文件时间时,必须考虑夏令时调整造成的1小时时差。

    5。 Windows版本

    所有Windows NT版本都支持NTFS。所有支持NTFS的Windows版本上的文件和目录的文件时间行为相同。

    但是如何解释FAT媒体上的文件时间取决于Windows的版本。在Windows Vista之前,FAT媒体上的文件时间与时区变化,夏令时设置和夏令时自动调整无关。在Windows 2000和Windows XP上,文件的上次修改日期/时间是固定的。

    但在Windows Vista及更高版本上,FAT媒体上显示的文件时间取决于夏令时和自动调整夏令时。时区并不重要。选择当前使用的另一个时区不会像在NTFS媒体上的文件那样更改FAT媒体上文件的显示文件时间。但是,如果当前时间在活动时区的夏令时期间并且启用了夏令时自动调整并且文件或目录的本地时间在夏令时期间,则Windows Vista会添加+1小时然后。在标准时间内在FAT驱动器上最后修改的文件在一年中是不变的;根据当前时间启用自动DST调整,只显示在DST期间内最后修改的文件是否带有+1小时。

    当在Windows 7计算机上的FAT32驱动器上拥有文件夹的公共共享并使用Windows XP计算机连接到此公用文件夹时,不同的FAT文件时间管理可能会非常混乱。最近修改于2015-09-19 17:39:08的文件存储在Windows 7 PC上的FAT32驱动器上今天由带有德语时区的Windows 7显示,文件时间为19.09.2015 17:39:08,但是在2个月内未经19.09.2015 16:39:08修改,Windows XP今天在连接的Windows 7 PC上显示相同的文件,将来文件时间为19.09.2015 17:39:08。

    将档案(ZIP,RAR,...)中存储的文件的文件时间与NTFS或FAT媒体上的文件进行比较可能真的是一场噩梦。

    6。将文件时间与当前时间进行比较

    对于此任务,将文件的上次修改时间与当前时间进行比较,仅考虑日期和时间格式设置就足够了。

    下面的批处理代码使用我在Batch file to delete files older than N days的答案中详细说明的代码。在使用下面的代码之前,应该阅读此解释。

    根据{strong> DATE TIME 的日期/时间格式以及%~tI为本地Windows PC上的文件返回的日期/时间字符串,可能需要对代码进行小的修改。批处理代码的注释行中给出了适当的提示。

    @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添加了有关如何使用wmic - Windows Management Instrumentation命令行实用程序忽略日期和时间格式设置,文件系统和Windows版本的信息。

    使用 wmic 具有固定格式的日期/时间字符串,因此批处理代码适用于所有Windows计算机,无需调整本地日期/时间格式。

    由于GetFileTime功能的内部使用(最有可能),因此使用 wmic 时,Windows的版本无关紧要。

    文件系统在评估本地时间/文件时间到UTC的分钟偏移量时才变得很重要。命令 wmic 仅在+***的FAT驱动器上返回分钟偏移到UTC,而分钟偏移对于NTFS驱动器上文件的最后修改文件时间是正确的。

    NTFS和FAT32驱动器上相同文件的示例:

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

    +120是CET(中欧时间)的+60分钟和有效夏令时的+60分钟的总和,换句话说,CEST(中欧夏令时)的+120分钟。

    因此,下面的批处理代码不会评估UTC的分钟偏移量,这是将文件的上次修改文件时间与当前本地时间进行比较时不需要的。

    此外,必须始终在 wmic 命令行中使用完整路径指定文件名。只指定当前目录中的文件的文件名或具有相对路径的文件名不起作用。并且每个反斜杠必须使用路径以文件名中的一个反斜杠进行转义。

    使用 wmic 的主要缺点是速度。根据{{​​3}}日志(几次运行的平均值),上面的批处理代码需要在我的计算机上大约50毫秒完成。调用 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
    

答案 1 :(得分:3)

或者你可以使用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所提到的,甚至可以构建一个hacky hybrid batch / 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 :(得分:2)

对此的解决方案很大程度上取决于预期的时间范围,因为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

答案 3 :(得分:2)

纯数批在日期数学上很糟糕。如果您的脚本在午夜后不久运行会发生什么?如果时间检查跨越夏令时更改怎么办?

我建议调用Windows脚本宿主并借用更好地处理日期/时间数学的语言。这是一个混合批处理/ 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混合脚本的更多信息,请see this GitHub page。上面使用的样式类似于该页面上的Hybrid.bat。这个答案是based on another。对于它的价值,PowerShell也擅长处理日期数学;但WSH的执行速度要快得多。

相关问题