在批处理文件中查找文件是否早于4小时。

9

简单地说,我怎样可以找出一个特定文件夹里的某个文件是否比X小时之前更旧?文件名和位置始终相同。

谢谢

4个回答

14

在比较文件时间与批处理代码时,必须考虑以下因素:

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

1. 日期和时间格式设置

有环境变量DATETIME,它们在访问时(而不是批处理执行开始时)以依赖于Windows语言和区域设置Windows区域和语言设置的格式返回当前日期和时间。国家定义了日期和时间格式。

甚至可以在那里定义例如德国短日期格式为DD.MM.YYYY(带前导零的日和月)和时间格式为HH:mm:ss(带有前导零的24小时制的小时、分钟和秒)。但这并不意味着DATETIME的值真正使用此格式。例如,即使定义了HH:mm:ss,当选择德国国家时,TIME的格式为H:mm:ss,ms,这意味着小时没有前导零,但是分钟和秒钟有前导零,并且时间字符串中还有毫秒。日期字符串也可以根据区域设置有或没有星期几。

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

最好是在早上10点之前使用以下批处理文件找出当前用户在当前计算机上的格式。

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

2. 存储介质的文件系统

在 Windows 文件系统中,有两种存储格式用于文件时间

  • 对于使用NTFS的存储介质,将使用NTFS精确时间。这是一个 64 位数字,表示自UTC时间1601年1月1日上午12:00以来已经过去的100纳秒间隔。

  • 对于使用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 驱动器上的文件。

根据 Windows 版本和 FAT 媒体上的文件,时区和夏令时设置可能很重要。有关详情,请参阅下文。


4. 夏令时的自动调整

有一个夏令时的自动调整设置,当启用时变得非常重要,因为默认情况下当前设置的时区定义了夏令时的调整。

例如,欧洲目前正在实行夏令时。在 Windows 的时钟设置中禁用此选项会导致 NTFS 媒体上显示的文件时间立即减少 1 小时,并且取决于 Windows 版本,许多 FAT 媒体上的文件也会发生变化。

由夏令时调整引起的这 1 小时时间差必须考虑在比较 NTFS 和 FAT 媒体上的文件时间时。


5. Windows 版本

NTFS 受到所有 Windows NT 版本的支持。在支持 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. 比较文件时间与当前时间

对于这个任务,比较文件的最后修改时间和当前时间,只需要考虑日期和时间格式设置即可。

下面的批处理代码使用了我在批处理文件删除N天前的文件中详细解释的代码。在使用下面的代码之前,请先阅读该解释。

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

@echo off
setlocal EnableExtensions DisableDelayedExpansion

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 not "%DateTime: AM=%" == "%DateTime%" (
    set "DateTime=%DateTime: AM=%"
) else if not "%DateTime: PM=%" == "%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%"  == "0" if not "%Month:~1%"  == "" set "Month=%Month:~1%"
if "%Day:~0,1%"    == "0" if not "%Day:~1%"    == "" set "Day=%Day:~1%"
if "%Hour:~0,1%"   == "0" if not "%Hour:~1%"   == "" set "Hour=%Hour:~1%"
if "%Minute:~0,1%" == "0" if not "%Minute:~1%" == "" set "Minute=%Minute:~1%"
if "%Second:~0,1%" == "0" if not "%Second:~1%" == "" 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 two 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 管理工具命令行实用程序来忽略日期和时间格式设置、文件系统和 Windows 版本的信息。

使用 wmic 的优点是日期/时间字符串具有固定格式,因此批处理代码可以在所有 Windows 计算机上运行,无需对本地日期/时间格式进行调整。

另外,使用 wmic 时不需要考虑 Windows 版本,因为它内部使用 GetFileTime 函数(很可能如此)。

文件系统只在还需计算本地时间/文件时间与 UTC 的分钟偏移量时才需要考虑。在 FAT 驱动器上,wmic 命令返回 UTC 偏移量的 +***,而在 NTFS 驱动器上,最后修改文件时间的分钟偏移量是正确的。

以下为同一文件在 NTFS 和 FAT32 驱动器上的示例:

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

+120是中欧时间(CET)加上活跃的夏令时(Daylight Saving Time)所组成的,也就是说中欧夏令时(CEST)为+120分钟。

以下批处理代码不会计算与UTC的分钟偏移量,因此在比较文件的最后修改时间和当前本地时间时不需要。

另外,在wmic命令行中必须始终指定文件名的完整路径。仅指定当前目录中的文件或带有相对路径的文件名是行不通的。并且文件名中的每个反斜杠都必须用一个额外的反斜杠进行转义。

使用wmic的主要缺点是速度较慢。根据Process Monitor日志记录(多次运行的平均值),以上批处理代码在我的Windows XP电脑上需要约50毫秒才能完成。而调用wmic两次的下面的批处理代码在Windows XP上完成同样的任务需要约1500毫秒(也是平均值)。在大量文件上使用wmic肯定不是好主意,至少在Windows XP上是这样,如果可以避免,因为本地日期/时间格式是已知的。

wmic解决方案的另一个缺点是,在完全新安装的Windows 11版本(构建22572及更高版本)上,wmic.exe不可用。请阅读Microsoft文档页面Windows客户端弃用功能,其中包含以下信息:

Windows管理规范命令行(WMIC)工具
WMIC工具在Windows 10版本21H1和Windows Server 21H1通用可用性渠道发布中已被弃用。此工具已被用于WMI的Windows PowerShell取代。注意:此弃用仅适用于命令行管理工具。WMI本身不受影响。

可以通过 设置 > 应用 > 可选功能 列表上的 WMIC 安装 wmic.exe,以在 Windows 10/11 上安装缺失于目录 %SystemRoot%\System32\wbem 的 WMIC。

@echo off
setlocal EnableExtensions DisableDelayedExpansion
if exist %SystemRoot%\System32\wbem\wmic.exe goto GetCurrent

echo ERROR: The WMI command-line utility wmic.exe is not installed.
echo(
echo There should not be used anymore batch files with WMIC on this computer.
echo(
echo There could be installed WMIC via Settings ^> Apps ^> Optional features.
echo(
pause
exit /B

:GetCurrent
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 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%  == 0 set "Month=%Month:~1%"
if %Day:~0,1%    == 0 set "Day=%Day:~1%"
if %Hour:~0,1%   == 0 set "Hour=%Hour:~1%"
if %Minute:~0,1% == 0 set "Minute=%Minute:~1%"
if %Second:~0,1% == 0 set "Second=%Second:~1%"

rem Must use two 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
你的文章和解释令人印象深刻,我给你点赞(+1)。只是想说一句,第1、2和5条可以通过使用WMI查询来否定,这些查询与语言环境和文件系统无关。用wmic os get localdatetime来获取当前日期和时间;用wmic datafile where "name='c:\\path\\to\\file.ext'" get lastmodified来获取最后修改时间,格式相同。两个结果均为GMT时间,并在值的末尾报告时区/DST偏移量。 - rojo
感谢@rojo提供的额外信息。我添加了一个使用__wmic__的代码示例,并提供了一些关于使用__wmic__的优缺点信息。 - Mofi
我必须说,你的回答给我留下了深刻印象。因此,我不得不再次为你开启悬赏。即使最终悬赏金额增加了2倍,这也是应该的。 - Paul
非常感谢@Paul。我认为,阅读了StackOverflow上许多“早于”问题后,现在是时候写下我所知道的关于Windows文件时间管理和文件时间比较的所有内容了。我希望这个详细的答案能够帮助到其他很多人。任何关于文件时间的额外输入随时都受欢迎。 - Mofi
2
不幸的是,使用 wmic 方法还有另一个问题:您无法指定包含 ,) 的文件路径 -- 请参见我的问题:如何在 WMIC 的 WHERE 子句中同时转义逗号和右括号? - aschipfl

4

或者您可以创建一个简单的程序,例如使用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

1
嗨@Martin +1,顺便说一句。也许脚本可以即时编译源代码。我已经看到过一个混合脚本可以做到这一点。我会去看看的。谢谢。 - Paul
1
@Paul,空格的问题来自于“for”命令。只需添加"delims="即可解决。我还按照你的建议添加了一个自编译版本的脚本。 - Martin
1
@Paul,我添加了一个参数来指定比较运算符。 - Martin
@Martin 太好了!如果我正确理解这段代码,如果我忽略“older”,那么它的意思是会处理相反的操作,并查找比n更新的最新文件,对吗? - Paul
1
@Paul 是的,只需传递一些不包含单词 older 的东西 - 例如 younger - Martin
显示剩余5条评论

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混合脚本的更多信息,请参见这个GitHub页面。上面使用的样式类似于该页面上的Hybrid.bat。本答案基于另一个答案。值得一提的是,PowerShell也擅长处理日期计算;但WSH执行速度更快。


2
这个问题的解决方案取决于期望的时间范围,因为据我所知,在批处理文件中你不能将时间作为一个整数而不使用外部程序。这意味着你必须在脚本中解析时间值,并且需要解析的数量和计算次数取决于你需要它工作的范围。
这里有一个简单的解决方案。它假设文件不可能早于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 XP测试中,第二个for循环没有产生预期的结果,因为使用了'"dir %filename%|find "%filename%""'而不是'dir "%filename%" ^| find "%filename%"'。结果是每个文件都比4小时旧。即使进行了这个更正,批处理代码也只能用于不包含路径的文件名,因为__dir__输出的只是文件名,但__find__搜索带有路径的文件名。 - Mofi
从__dir__的输出中过滤并通过__for__解析,使用修正后的'dir "%filename%" ^| find "%filename%"'从指定文件获取最后修改时间的下一个问题是,该文件必须没有路径,因此在当前目录中。 __find__搜索时没有参数/I是区分大小写的。因此,如果第二行未精确拼写存储在存储介质上的文件名(不带路径),则__find__将始终无法在__dir__的输出中找到该文件的行。我还看到这段代码存在其他问题。抱歉! - Mofi
@mofi 我从未在XP上测试过这个脚本,正如你在自己的答案中所解释的那样,时间/日期/修改日期解析方法与操作系统版本和区域设置非常紧密相关。我在我的答案中指出这是一个简单的解决方案,但当我在2011年发布时,它确实在win7上运行良好 ;) - Superole
@Mofi,有趣的是:就在前几天,我还考虑过更新我的答案,使用wmic代替%date%,%time%,dir和find。但是现在看到你的答案,我想我不会再费心了;) 很抱歉我的解决方案对你没有用。 - Superole
运行此批处理文件时,对于在同一天修改的文件的读者的一些额外信息: 1. 分配给环境变量 filename 的文件名不能包含空格或这些字符之一:&()[]{}^=;!'+,`~。 __2.__ 可以从代码中删除 set day=%%a&set mon=%%b&set yr=%%c&set fday=%%a&set fmon=%%b&set fyr=%%c&,因为只使用了小时和分钟环境变量来计算时间差异。 __3.__ 如果一个小时或一分钟是 0809,则代码不会正确工作,因为它们被视为无效的八进制数,并被处理为 0 - Mofi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接